mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-08-13 04:42:12 +03:00
267 lines
7.5 KiB
C++
267 lines
7.5 KiB
C++
#ifndef ECODE_PROJECTSEARCH_HPP
|
|
#define ECODE_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;
|
|
|
|
namespace ecode {
|
|
|
|
using GlobMatch = std::pair<std::string, bool>; // where string is the glob and bool true
|
|
// indicates that it's inverted / negated
|
|
|
|
class ProjectSearch {
|
|
public:
|
|
struct SearchConfig {
|
|
std::string searchString;
|
|
bool caseSensitive{ false };
|
|
bool wholeWord{ false };
|
|
TextDocument::FindReplaceType type{ TextDocument::FindReplaceType::Normal };
|
|
|
|
SearchConfig() {}
|
|
|
|
SearchConfig( std::string searchString, bool caseSensitive, bool wholeWord,
|
|
TextDocument::FindReplaceType type ) :
|
|
searchString( std::move( searchString ) ),
|
|
caseSensitive( caseSensitive ),
|
|
wholeWord( wholeWord ),
|
|
type( type ) {}
|
|
};
|
|
|
|
struct ResultData {
|
|
struct Result {
|
|
Result( const String& line, const TextRange& pos, Int64 s, Int64 e ) :
|
|
line( line ), position( pos ), start( s ), end( e ) {}
|
|
Result() {}
|
|
String line;
|
|
TextRange position;
|
|
Int64 start{ 0 };
|
|
Int64 end{ 0 };
|
|
bool selected{ true };
|
|
std::vector<std::string> captures;
|
|
};
|
|
std::string file;
|
|
std::vector<Result> results;
|
|
std::shared_ptr<TextDocument> openDoc{ nullptr };
|
|
bool selected{ true };
|
|
|
|
ResultData() {}
|
|
|
|
ResultData( std::string&& file, std::vector<Result>&& results,
|
|
std::shared_ptr<TextDocument> openDoc = {}, bool selected = true ) :
|
|
file( std::move( file ) ),
|
|
results( std::move( results ) ),
|
|
openDoc( std::move( openDoc ) ),
|
|
selected( selected ) {}
|
|
|
|
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;
|
|
}
|
|
};
|
|
|
|
using Result = std::vector<ResultData>;
|
|
using ConsolidatedResult = std::pair<SearchConfig, Result>;
|
|
using ResultCb = std::function<void( const ConsolidatedResult& )>;
|
|
|
|
class ResultModel : public Model {
|
|
public:
|
|
enum Column {
|
|
FileOrPosition,
|
|
Line,
|
|
LineEnd,
|
|
ColumnStart,
|
|
ColumnEnd,
|
|
Selected,
|
|
Data,
|
|
ChildCount
|
|
};
|
|
|
|
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.internalData() )
|
|
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";
|
|
}
|
|
|
|
size_t resultCount() {
|
|
size_t count = 0;
|
|
for ( const auto& res : mResult )
|
|
count += res.results.size();
|
|
return count;
|
|
}
|
|
|
|
Variant data( const ModelIndex& index, ModelRole role = ModelRole::Display ) const {
|
|
static const char* EMPTY = "";
|
|
if ( role == ModelRole::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( String::format( "%6lld ", mResult[index.internalId()]
|
|
.results[index.row()]
|
|
.position.start()
|
|
.line() +
|
|
1 ) ) +
|
|
mResult[index.internalId()].results[index.row()].line );
|
|
}
|
|
}
|
|
} else if ( role == ModelRole::Custom ) {
|
|
if ( index.internalId() != -1 ) {
|
|
switch ( index.column() ) {
|
|
case FileOrPosition:
|
|
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 ColumnStart:
|
|
return Variant( mResult[index.internalId()]
|
|
.results[index.row()]
|
|
.position.start()
|
|
.column() );
|
|
case ColumnEnd:
|
|
return Variant( mResult[index.internalId()]
|
|
.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()] );
|
|
case ChildCount:
|
|
return Variant( (Int64)0 );
|
|
}
|
|
} else {
|
|
switch ( index.column() ) {
|
|
case FileOrPosition:
|
|
return Variant( mResult[index.row()].file.c_str() );
|
|
case Data:
|
|
return Variant( (void*)&mResult[index.row()] );
|
|
case ChildCount:
|
|
return Variant( (Int64)mResult[index.row()].results.size() );
|
|
case Selected:
|
|
return Variant( (bool)mResult[index.row()].selected );
|
|
}
|
|
}
|
|
}
|
|
return Variant( EMPTY );
|
|
}
|
|
|
|
const Result& getResult() const { return mResult; }
|
|
|
|
void removeLastNewLineCharacter();
|
|
|
|
void setResultFromSymbolReference( bool ref ) { mResultFromSymbolReference = ref; }
|
|
|
|
bool isResultFromSymbolReference() const { return mResultFromSymbolReference; }
|
|
|
|
void setOpType( TextDocument::FindReplaceType opType ) { mOpType = opType; }
|
|
|
|
bool isResultFromPatternMatch() const {
|
|
return mOpType != TextDocument::FindReplaceType::Normal;
|
|
}
|
|
|
|
protected:
|
|
Result mResult;
|
|
TextDocument::FindReplaceType mOpType{ TextDocument::FindReplaceType::Normal };
|
|
bool mResultFromSymbolReference{ false };
|
|
bool mResultFromLuaPattern{ false };
|
|
bool mResultFromRegEx{ false };
|
|
};
|
|
|
|
static std::shared_ptr<ResultModel> asModel( const Result& result ) {
|
|
return std::make_shared<ResultModel>( result );
|
|
}
|
|
|
|
static std::vector<ProjectSearch::ResultData::Result>
|
|
fileResFromDoc( const std::string& string, bool caseSensitive, bool wholeWord,
|
|
TextDocument::FindReplaceType type, std::shared_ptr<TextDocument> doc );
|
|
|
|
struct FindData {
|
|
Mutex resMutex;
|
|
Mutex countMutex;
|
|
int resCount{ 0 };
|
|
ProjectSearch::Result res;
|
|
bool active{ true };
|
|
Uint64 taskTag{ 0 };
|
|
};
|
|
|
|
static FindData*
|
|
find( const std::vector<std::string> files, std::string string,
|
|
std::shared_ptr<ThreadPool> pool, ResultCb result, bool caseSensitive,
|
|
bool wholeWord = false,
|
|
const TextDocument::FindReplaceType& type = TextDocument::FindReplaceType::Normal,
|
|
const std::vector<GlobMatch>& pathFilters = {}, std::string basePath = "",
|
|
std::vector<std::shared_ptr<TextDocument>> openDocs = {} );
|
|
};
|
|
|
|
} // namespace ecode
|
|
|
|
#endif // ECODE_PROJECTSEARCH_HPP
|