diff --git a/include/eepp/ui/doc/textdocument.hpp b/include/eepp/ui/doc/textdocument.hpp index 244a95765..5e4f05eca 100644 --- a/include/eepp/ui/doc/textdocument.hpp +++ b/include/eepp/ui/doc/textdocument.hpp @@ -47,6 +47,27 @@ class EE_API TextDocument { TextRange result{}; std::vector captures{}; bool isValid() const { return result.isValid(); } + bool operator==( const SearchResult& other ) { + return result == other.result && captures == other.captures; + } + }; + + class SearchResults : public std::vector { + public: + bool isSorted() const { return mIsSorted; } + + void setSorted() { mIsSorted = true; } + + TextRanges ranges() const { + TextRanges ranges; + ranges.reserve( size() ); + for ( const auto& r : *this ) + ranges.push_back( r.result ); + return ranges; + } + + protected: + bool mIsSorted{ false }; }; enum class MatchDirection { Forward, Backward }; @@ -360,9 +381,9 @@ class EE_API TextDocument { FindReplaceType type = FindReplaceType::Normal, TextRange restrictRange = TextRange() ); - TextRanges findAll( const String& text, bool caseSensitive = true, bool wholeWord = false, - FindReplaceType type = FindReplaceType::Normal, - TextRange restrictRange = TextRange(), size_t maxResults = 0 ); + SearchResults findAll( const String& text, bool caseSensitive = true, bool wholeWord = false, + FindReplaceType type = FindReplaceType::Normal, + TextRange restrictRange = TextRange(), size_t maxResults = 0 ); int replaceAll( const String& text, const String& replace, const bool& caseSensitive = true, const bool& wholeWord = false, FindReplaceType type = FindReplaceType::Normal, diff --git a/include/eepp/ui/tools/uicodeeditorsplitter.hpp b/include/eepp/ui/tools/uicodeeditorsplitter.hpp index 559cebbe9..bc57dd1d6 100644 --- a/include/eepp/ui/tools/uicodeeditorsplitter.hpp +++ b/include/eepp/ui/tools/uicodeeditorsplitter.hpp @@ -174,6 +174,8 @@ class EE_API UICodeEditorSplitter { void forEachDoc( std::function run ) const; + void forEachDocSharedPtr( std::function doc )> run ) const; + void forEachTabWidget( std::function run ) const; void zoomIn(); diff --git a/src/eepp/ui/doc/textdocument.cpp b/src/eepp/ui/doc/textdocument.cpp index c0a01c517..85b9669c2 100644 --- a/src/eepp/ui/doc/textdocument.cpp +++ b/src/eepp/ui/doc/textdocument.cpp @@ -1920,8 +1920,8 @@ void TextDocument::selectAllWords() { { getBottomMostCursor().normalized().end(), endOfDoc() } ) ); if ( !res.empty() ) { for ( auto& selection : res ) - selection.reverse(); - addSelections( std::move( res ) ); + selection.result.reverse(); + addSelections( res.ranges() ); } } @@ -2691,10 +2691,10 @@ bool TextDocument::isInsertingText() const { return mInsertingText; } -TextRanges TextDocument::findAll( const String& text, bool caseSensitive, bool wholeWord, - FindReplaceType type, TextRange restrictRange, - size_t maxResults ) { - TextRanges all; +TextDocument::SearchResults TextDocument::findAll( const String& text, bool caseSensitive, + bool wholeWord, FindReplaceType type, + TextRange restrictRange, size_t maxResults ) { + SearchResults all; TextDocument::SearchResult found; TextPosition from = startOfDoc(); auto stopFlagUP = std::make_unique( false ); @@ -2709,10 +2709,10 @@ TextRanges TextDocument::findAll( const String& text, bool caseSensitive, bool w do { found = find( text, from, caseSensitive, wholeWord, type, restrictRange ); if ( found.isValid() ) { - if ( !all.empty() && all.back() == found.result ) + if ( !all.empty() && all.back() == found ) break; from = found.result.end(); - all.push_back( found.result ); + all.push_back( found ); if ( ( maxResults != 0 && all.size() >= maxResults ) || *stopFlag ) break; } @@ -2814,11 +2814,11 @@ void TextDocument::selectAllMatches() { if ( !hasSelection() ) return; auto sel = getSelection(); - TextRanges ranges = findAll( getSelectedText(), true, false ); + auto ranges = findAll( getSelectedText(), true, false ); for ( const auto& range : ranges ) { - if ( sel == range || sel.normalized() == range ) + if ( sel == range.result || sel.normalized() == range.result ) continue; - addSelection( range.reversed() ); + addSelection( range.result.reversed() ); } } diff --git a/src/eepp/ui/tools/uicodeeditorsplitter.cpp b/src/eepp/ui/tools/uicodeeditorsplitter.cpp index ecf3bc754..4d996f423 100644 --- a/src/eepp/ui/tools/uicodeeditorsplitter.cpp +++ b/src/eepp/ui/tools/uicodeeditorsplitter.cpp @@ -814,6 +814,16 @@ void UICodeEditorSplitter::forEachDoc( std::function run run( *doc ); } +void UICodeEditorSplitter::forEachDocSharedPtr( + std::function )> run ) const { + std::unordered_map> docs; + forEachEditor( [&docs]( UICodeEditor* editor ) { + docs.insert( { editor->getDocumentRef().get(), editor->getDocumentRef() } ); + } ); + for ( const auto& doc : docs ) + run( doc.second ); +} + void UICodeEditorSplitter::forEachTabWidget( std::function run ) const { for ( auto widget : mTabWidgets ) run( widget ); diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index 0c72ba71e..e5a0d4f41 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -2834,7 +2834,7 @@ void UICodeEditor::updateHighlightWordCache() { { Lock l( mHighlightWordCacheMutex ); - mHighlightWordCache = std::move( wordCache ); + mHighlightWordCache = wordCache.ranges(); } Log::info( "Document search triggered in document: \"%s\", searched for " @@ -2853,7 +2853,8 @@ void UICodeEditor::updateHighlightWordCache() { mDoc->findAll( mHighlightWord.escapeSequences ? String::unescape( mHighlightWord.text ) : mHighlightWord.text, mHighlightWord.caseSensitive, mHighlightWord.wholeWord, - mHighlightWord.type, mHighlightWord.range, 100 ); + mHighlightWord.type, mHighlightWord.range, 100 ) + .ranges(); } } diff --git a/src/tools/ecode/docsearchcontroller.cpp b/src/tools/ecode/docsearchcontroller.cpp index fcc8b4cde..4c0b56b6d 100644 --- a/src/tools/ecode/docsearchcontroller.cpp +++ b/src/tools/ecode/docsearchcontroller.cpp @@ -337,10 +337,10 @@ void DocSearchController::selectAll( SearchState& search ) { search.editor->getDocument().setActiveClient( search.editor ); mLastSearch = search.text; TextDocument& doc = search.editor->getDocument(); - TextRanges ranges = doc.findAll( search.text, search.caseSensitive, search.wholeWord, - search.type, search.range ); + auto ranges = doc.findAll( search.text, search.caseSensitive, search.wholeWord, search.type, + search.range ); for ( const auto& range : ranges ) - doc.addSelection( range.reversed() ); + doc.addSelection( range.result.reversed() ); } int DocSearchController::replaceAll( SearchState& search, const String& replace ) { diff --git a/src/tools/ecode/globalsearchcontroller.cpp b/src/tools/ecode/globalsearchcontroller.cpp index 1118a61c4..0aae91630 100644 --- a/src/tools/ecode/globalsearchcontroller.cpp +++ b/src/tools/ecode/globalsearchcontroller.cpp @@ -73,7 +73,6 @@ size_t GlobalSearchController::replaceInFiles( const std::string& replaceText, for ( const auto& result : fileResult.results ) { if ( !result.selected ) continue; - replacements.push_back( { result.start, result.end } ); std::string newText( replaceText ); LuaPattern ptrn( "$(%d+)" ); for ( auto& match : ptrn.gmatch( replaceText ) ) { @@ -86,6 +85,16 @@ size_t GlobalSearchController::replaceInFiles( const std::string& replaceText, replaceTexts.emplace_back( std::move( newText ) ); } } + + if ( result.openDoc ) { + auto oldSel = result.openDoc->getSelections(); + result.openDoc->setSelection( result.position ); + result.openDoc->replaceSelection( replaceTexts[replaceTexts.size() - 1] ); + result.openDoc->setSelection( oldSel ); + count++; + } else { + replacements.push_back( { result.start, result.end } ); + } } if ( replacements.size() == replaceTexts.size() && @@ -100,10 +109,19 @@ size_t GlobalSearchController::replaceInFiles( const std::string& replaceText, std::vector> replacements; for ( const auto& result : fileResult.results ) - if ( result.selected ) - replacements.push_back( { result.start, result.end } ); + if ( result.selected ) { + if ( result.openDoc ) { + auto oldSel = result.openDoc->getSelections(); + result.openDoc->setSelection( result.position ); + result.openDoc->replaceSelection( replaceText ); + result.openDoc->setSelection( oldSel ); + count++; + } else { + replacements.push_back( { result.start, result.end } ); + } + } - if ( replaceInFile( fileResult.file, replaceText, replacements ) ) + if ( !replacements.empty() && replaceInFile( fileResult.file, replaceText, replacements ) ) count += replacements.size(); } @@ -597,6 +615,11 @@ void GlobalSearchController::doGlobalSearch( String text, String filter, bool ca if ( escapeSequence ) text.unescape(); std::string search( text.toUtf8() ); + + std::vector> openDocs; + mSplitter->forEachDocSharedPtr( + [&openDocs]( auto doc ) { openDocs.emplace_back( std::move( doc ) ); } ); + ProjectSearch::find( mApp->getDirTree()->getFiles(), search, #if EE_PLATFORM != EE_PLATFORM_EMSCRIPTEN || defined( __EMSCRIPTEN_PTHREADS__ ) @@ -621,7 +644,7 @@ void GlobalSearchController::doGlobalSearch( String text, String filter, bool ca caseSensitive, wholeWord, luaPattern ? TextDocument::FindReplaceType::LuaPattern : TextDocument::FindReplaceType::Normal, - parseGlobMatches( filter ), mApp->getCurrentProject() ); + parseGlobMatches( filter ), mApp->getCurrentProject(), openDocs ); } } diff --git a/src/tools/ecode/projectsearch.cpp b/src/tools/ecode/projectsearch.cpp index 9a8cc5f34..4ba906555 100644 --- a/src/tools/ecode/projectsearch.cpp +++ b/src/tools/ecode/projectsearch.cpp @@ -145,7 +145,8 @@ searchInFileLuaPattern( const std::string& file, const std::string& text, const void ProjectSearch::find( const std::vector files, const std::string& string, ResultCb result, bool caseSensitive, bool wholeWord, const TextDocument::FindReplaceType& type, - const std::vector& pathFilters, std::string basePath ) { + const std::vector& pathFilters, std::string basePath, + std::vector> openDocs ) { Result res; const auto occ = type == TextDocument::FindReplaceType::Normal @@ -186,14 +187,15 @@ struct FindData { void ProjectSearch::find( const std::vector files, std::string string, std::shared_ptr pool, ResultCb result, bool caseSensitive, bool wholeWord, const TextDocument::FindReplaceType& type, - const std::vector& pathFilters, std::string basePath ) { + const std::vector& pathFilters, std::string basePath, + std::vector> openDocs ) { if ( files.empty() ) result( {} ); FileSystem::dirAddSlashAtEnd( basePath ); pool->run( [files = std::move( files ), string = std::move( string ), pool = std::move( pool ), result = std::move( result ), caseSensitive, wholeWord, type, - pathFilters = std::move( pathFilters ), - basePath = std::move( basePath )]() mutable { + pathFilters = std::move( pathFilters ), basePath = std::move( basePath ), + openDocs = std::move( openDocs )]() mutable { FindData* findData = eeNew( FindData, () ); findData->resCount = files.size(); if ( !caseSensitive ) @@ -235,40 +237,87 @@ void ProjectSearch::find( const std::vector files, std::string stri return; } + std::unordered_map> openPaths; + for ( const auto& doc : openDocs ) + if ( doc->isDirty() ) + openPaths.insert( { doc->getFilePath(), doc } ); + pos = 0; for ( const auto& file : files ) { if ( !search[pos] ) { pos++; continue; } + pos++; - pool->run( - [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 } ); - } - }, - [result, findData]( const auto& ) { - int count; - { - Lock l( findData->countMutex ); - findData->resCount--; - count = findData->resCount; - } - if ( count == 0 ) { - result( findData->res ); - eeDelete( findData ); + const auto onSearchEnd = [result, findData]( const auto& ) { + int count; + { + Lock l( findData->countMutex ); + findData->resCount--; + count = findData->resCount; + } + if ( count == 0 ) { + result( findData->res ); + eeDelete( findData ); #if EE_PLATFORM == EE_PLATFORM_LINUX - malloc_trim( 0 ); + malloc_trim( 0 ); #endif - } - } ); + } + }; + + auto openPath = openPaths.find( file ); + bool openDoc = openPath != openPaths.end(); + std::shared_ptr doc = nullptr; + if ( openDoc ) + doc = openPath->second; + + if ( openDoc && openPath->second->isDirty() ) { + pool->run( + [findData, doc, string, caseSensitive, wholeWord, type] { + auto res = doc->findAll( string, caseSensitive, wholeWord, type ); + std::vector fileRes; + for ( const auto& r : res ) { + ProjectSearch::ResultData::Result f; + f.openDoc = doc; + f.position = r.result; + const auto& line = doc->line( r.result.start().line() ); + if ( line.size() > EE_1KB ) + f.line = line.getText().substr( 0, EE_1KB ); + else + f.line = line.getTextWithoutNewLine(); + f.start = r.result.start().column(); + f.end = r.result.end().column(); + std::vector captures; + for ( const auto& capture : r.captures ) + captures.emplace_back( doc->getText( capture ).toUtf8() ); + f.captures = std::move( captures ); + fileRes.emplace_back( std::move( f ) ); + } + + if ( !fileRes.empty() ) { + Lock l( findData->resMutex ); + std::string file( doc->getFilePath() ); + findData->res.push_back( { std::move( file ), std::move( fileRes ) } ); + } + }, + onSearchEnd ); + } else { + pool->run( + [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( { std::move( file ), std::move( fileRes ) } ); + } + }, + onSearchEnd ); + } } } ); } diff --git a/src/tools/ecode/projectsearch.hpp b/src/tools/ecode/projectsearch.hpp index 4ac1b74f2..72c7320e5 100644 --- a/src/tools/ecode/projectsearch.hpp +++ b/src/tools/ecode/projectsearch.hpp @@ -32,6 +32,7 @@ class ProjectSearch { Int64 end{ 0 }; bool selected{ true }; std::vector captures; + std::shared_ptr openDoc; }; std::string file; std::vector results; @@ -212,14 +213,16 @@ class ProjectSearch { find( const std::vector files, const std::string& string, ResultCb result, bool caseSensitive, bool wholeWord = false, const TextDocument::FindReplaceType& type = TextDocument::FindReplaceType::Normal, - const std::vector& pathFilters = {}, std::string basePath = "" ); + const std::vector& pathFilters = {}, std::string basePath = "", + std::vector> openDocs = {} ); 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, - const std::vector& pathFilters = {}, std::string basePath = "" ); + const std::vector& pathFilters = {}, std::string basePath = "", + std::vector> openDocs = {} ); }; } // namespace ecode