mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-10-02 11:10:31 +03:00
Project search filters WIP.
This commit is contained in:
@@ -398,6 +398,18 @@ class EE_API String {
|
|||||||
/** @return The next character in a utf8 null terminated string */
|
/** @return The next character in a utf8 null terminated string */
|
||||||
static Uint32 utf8Next( char*& utf8String );
|
static Uint32 utf8Next( char*& utf8String );
|
||||||
|
|
||||||
|
/** glob matches a string against a glob
|
||||||
|
** @return True if matches
|
||||||
|
*/
|
||||||
|
static bool globMatch( const std::string& text, const std::string_view& glob,
|
||||||
|
bool caseInsensitive = false );
|
||||||
|
|
||||||
|
/** glob matches a string against a set of globs
|
||||||
|
** @return True if matches
|
||||||
|
*/
|
||||||
|
static bool globMatch( const std::string& text, const std::vector<std::string>& globs,
|
||||||
|
bool caseInsensitive = false );
|
||||||
|
|
||||||
/** @brief Default constructor
|
/** @brief Default constructor
|
||||||
** This constructor creates an empty string.
|
** This constructor creates an empty string.
|
||||||
**/
|
**/
|
||||||
|
|||||||
+148
-2
@@ -14,6 +14,152 @@ namespace EE {
|
|||||||
|
|
||||||
const std::size_t String::InvalidPos = StringType::npos;
|
const std::size_t String::InvalidPos = StringType::npos;
|
||||||
|
|
||||||
|
// Author: Robert A. van Engelen, engelen@genivia.com
|
||||||
|
// Date: August 5, 2019
|
||||||
|
// License: The Code Project Open License (CPOL)
|
||||||
|
// https://www.codeproject.com/info/cpol10.aspx
|
||||||
|
|
||||||
|
#define DOTGLOB 1
|
||||||
|
#define PATHSEP '/'
|
||||||
|
#define CASE( c, caseInsensitive ) ( caseInsensitive ? std::tolower( c ) : ( c ) )
|
||||||
|
|
||||||
|
bool String::globMatch( const std::string& text, const std::string_view& glob,
|
||||||
|
bool caseInsensitive ) {
|
||||||
|
size_t i = 0;
|
||||||
|
size_t j = 0;
|
||||||
|
size_t n = text.size();
|
||||||
|
size_t m = glob.size();
|
||||||
|
size_t text1_backup = std::string::npos;
|
||||||
|
size_t glob1_backup = std::string::npos;
|
||||||
|
size_t text2_backup = std::string::npos;
|
||||||
|
size_t glob2_backup = std::string::npos;
|
||||||
|
bool nodot = !DOTGLOB;
|
||||||
|
// match pathname if glob contains a / otherwise match the basename
|
||||||
|
if ( j + 1 < m && glob[j] == '/' ) {
|
||||||
|
// if pathname starts with ./ then ignore these pairs
|
||||||
|
while ( i + 1 < n && text[i] == '.' && text[i + 1] == PATHSEP )
|
||||||
|
i += 2;
|
||||||
|
// if pathname starts with a / then ignore it
|
||||||
|
if ( i < n && text[i] == PATHSEP )
|
||||||
|
i++;
|
||||||
|
j++;
|
||||||
|
} else if ( glob.find( '/' ) == std::string::npos ) {
|
||||||
|
size_t sep = text.rfind( PATHSEP );
|
||||||
|
if ( sep != std::string::npos )
|
||||||
|
i = sep + 1;
|
||||||
|
}
|
||||||
|
while ( i < n ) {
|
||||||
|
if ( j < m ) {
|
||||||
|
switch ( glob[j] ) {
|
||||||
|
case '*':
|
||||||
|
// match anything except . after /
|
||||||
|
if ( nodot && text[i] == '.' )
|
||||||
|
break;
|
||||||
|
if ( ++j < m && glob[j] == '*' ) {
|
||||||
|
// trailing ** match everything after /
|
||||||
|
if ( ++j >= m )
|
||||||
|
return true;
|
||||||
|
// ** followed by a / match zero or more directories
|
||||||
|
if ( glob[j] != '/' )
|
||||||
|
return false;
|
||||||
|
// new **-loop, discard *-loop
|
||||||
|
text1_backup = std::string::npos;
|
||||||
|
glob1_backup = std::string::npos;
|
||||||
|
text2_backup = i;
|
||||||
|
glob2_backup = ++j;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// trailing * matches everything except /
|
||||||
|
text1_backup = i;
|
||||||
|
glob1_backup = j;
|
||||||
|
continue;
|
||||||
|
case '?':
|
||||||
|
// match anything except . after /
|
||||||
|
if ( nodot && text[i] == '.' )
|
||||||
|
break;
|
||||||
|
// match any character except /
|
||||||
|
if ( text[i] == PATHSEP )
|
||||||
|
break;
|
||||||
|
i++;
|
||||||
|
j++;
|
||||||
|
continue;
|
||||||
|
case '[': {
|
||||||
|
// match anything except . after /
|
||||||
|
if ( nodot && text[i] == '.' )
|
||||||
|
break;
|
||||||
|
// match any character in [...] except /
|
||||||
|
if ( text[i] == PATHSEP )
|
||||||
|
break;
|
||||||
|
int lastchr;
|
||||||
|
bool matched = false;
|
||||||
|
bool reverse = j + 1 < m && ( glob[j + 1] == '^' || glob[j + 1] == '!' );
|
||||||
|
// inverted character class
|
||||||
|
if ( reverse )
|
||||||
|
j++;
|
||||||
|
// match character class
|
||||||
|
for ( lastchr = 256; ++j < m && glob[j] != ']';
|
||||||
|
lastchr = CASE( glob[j], caseInsensitive ) )
|
||||||
|
if ( lastchr < 256 && glob[j] == '-' && j + 1 < m && glob[j + 1] != ']'
|
||||||
|
? CASE( text[i], caseInsensitive ) <=
|
||||||
|
CASE( glob[++j], caseInsensitive ) &&
|
||||||
|
CASE( text[i], caseInsensitive ) >= lastchr
|
||||||
|
: CASE( text[i], caseInsensitive ) ==
|
||||||
|
CASE( glob[j], caseInsensitive ) )
|
||||||
|
matched = true;
|
||||||
|
if ( matched == reverse )
|
||||||
|
break;
|
||||||
|
i++;
|
||||||
|
if ( j < m )
|
||||||
|
j++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
case '\\':
|
||||||
|
// literal match \-escaped character
|
||||||
|
if ( j + 1 < m )
|
||||||
|
j++;
|
||||||
|
// FALLTHROUGH
|
||||||
|
default:
|
||||||
|
// match the current non-NUL character
|
||||||
|
if ( CASE( glob[j], caseInsensitive ) != CASE( text[i], caseInsensitive ) &&
|
||||||
|
!( glob[j] == '/' && text[i] == PATHSEP ) )
|
||||||
|
break;
|
||||||
|
// do not match a . with *, ? [] after /
|
||||||
|
nodot = !DOTGLOB && glob[j] == '/';
|
||||||
|
i++;
|
||||||
|
j++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( glob1_backup != std::string::npos && text[text1_backup] != PATHSEP ) {
|
||||||
|
// *-loop: backtrack to the last * but do not jump over /
|
||||||
|
i = ++text1_backup;
|
||||||
|
j = glob1_backup;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( glob2_backup != std::string::npos ) {
|
||||||
|
// **-loop: backtrack to the last **
|
||||||
|
i = ++text2_backup;
|
||||||
|
j = glob2_backup;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// ignore trailing stars
|
||||||
|
while ( j < m && glob[j] == '*' )
|
||||||
|
j++;
|
||||||
|
// at end of text means success if nothing else is left to match
|
||||||
|
return j >= m;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool String::globMatch( const std::string& text, const std::vector<std::string>& globs,
|
||||||
|
bool caseInsensitive ) {
|
||||||
|
for ( const auto& glob : globs ) {
|
||||||
|
if ( globMatch( text, glob, caseInsensitive ) )
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Originally written by Joel Yliluoma <joel.yliluoma@iki.fi>
|
* Originally written by Joel Yliluoma <joel.yliluoma@iki.fi>
|
||||||
* http://en.wikipedia.org/wiki/Talk:Boyer%E2%80%93Moore_string_search_algorithm#Common_functions
|
* http://en.wikipedia.org/wiki/Talk:Boyer%E2%80%93Moore_string_search_algorithm#Common_functions
|
||||||
@@ -919,7 +1065,7 @@ void String::numberCleanInPlace( std::string& strNumber ) {
|
|||||||
std::string String::fromFloat( const Float& value, const std::string& append,
|
std::string String::fromFloat( const Float& value, const std::string& append,
|
||||||
const std::string& prepend, size_t digitsAfterComma ) {
|
const std::string& prepend, size_t digitsAfterComma ) {
|
||||||
std::ostringstream ss;
|
std::ostringstream ss;
|
||||||
ss.precision(digitsAfterComma);
|
ss.precision( digitsAfterComma );
|
||||||
ss << std::fixed << value;
|
ss << std::fixed << value;
|
||||||
std::string val( ss.str() );
|
std::string val( ss.str() );
|
||||||
numberCleanInPlace( val );
|
numberCleanInPlace( val );
|
||||||
@@ -929,7 +1075,7 @@ std::string String::fromFloat( const Float& value, const std::string& append,
|
|||||||
std::string String::fromDouble( const double& value, const std::string& append,
|
std::string String::fromDouble( const double& value, const std::string& append,
|
||||||
const std::string& prepend, size_t digitsAfterComma ) {
|
const std::string& prepend, size_t digitsAfterComma ) {
|
||||||
std::ostringstream ss;
|
std::ostringstream ss;
|
||||||
ss.precision(digitsAfterComma);
|
ss.precision( digitsAfterComma );
|
||||||
ss << std::fixed << value;
|
ss << std::fixed << value;
|
||||||
std::string val( ss.str() );
|
std::string val( ss.str() );
|
||||||
numberCleanInPlace( val );
|
numberCleanInPlace( val );
|
||||||
|
|||||||
@@ -498,15 +498,24 @@ R"html(
|
|||||||
</locatebar>
|
</locatebar>
|
||||||
<globalsearchbar id="global_search_bar" lw="mp" lh="wc">
|
<globalsearchbar id="global_search_bar" lw="mp" lh="wc">
|
||||||
<hbox lw="mp" lh="wc">
|
<hbox lw="mp" lh="wc">
|
||||||
<TextView text='@string(search_for, "Search for:")' margin-right="4dp" />
|
<vbox lw="wc" lh="wc">
|
||||||
|
<TextView lh="18dp" text='@string(search_for, "Search for:")' margin-right="4dp" margin-bottom="2dp" />
|
||||||
|
<TextView lh="18dp" text='@string(path_filters, "Path Filters:")' margin-right="4dp" />
|
||||||
|
</vbox>
|
||||||
<vbox lw="0" lw8="1" lh="wc">
|
<vbox lw="0" lw8="1" lh="wc">
|
||||||
<TextInput id="global_search_find" lw="mp" lh="wc" lh="18dp" padding="0" margin-bottom="2dp" />
|
<TextInput id="global_search_find" lw="mp" lh="wc" lh="18dp" padding="0" margin-bottom="2dp" />
|
||||||
<hbox lw="mp" lh="wc" margin-bottom="4dp">
|
<hbox lw="mp" lh="wc" margin-bottom="4dp">
|
||||||
<CheckBox id="case_sensitive" text='@string(case_sensitive, "Case sensitive")' selected="true" />
|
<TextInput id="global_search_where" lw="0" lw8="1" lh="wc" lh="18dp" padding="0" margin-right="4dp"
|
||||||
<CheckBox id="whole_word" text='@string(match_whole_word, "Match Whole Word")' selected="false" margin-left="8dp" />
|
hint='@string(search_where_example, "e.g. *.ts, src/**/include, -src/**/exclude")' hint-display="focus" />
|
||||||
<CheckBox id="lua_pattern" text='@string(lua_pattern, "Lua Pattern")' selected="false" margin-left="8dp" />
|
<PushButton lw="wc" lh="mp" text="..." />
|
||||||
<CheckBox id="escape_sequence" text='@string(use_escape_sequences, "Use escape sequences")' margin-left="8dp" selected="false" tooltip='@string(escape_sequence_tooltip, "Replace \\, \t, \n, \r and \uXXXX (Unicode characters) with the corresponding control")' />
|
|
||||||
</hbox>
|
</hbox>
|
||||||
|
<StackLayout lw="mp" lh="wc" margin-bottom="4dp">
|
||||||
|
<CheckBox id="case_sensitive" text='@string(case_sensitive, "Case sensitive")' selected="true" margin-right="8dp" />
|
||||||
|
<CheckBox id="whole_word" text='@string(match_whole_word, "Match Whole Word")' selected="false" margin-right="8dp" />
|
||||||
|
<CheckBox id="lua_pattern" text='@string(lua_pattern, "Lua Pattern")' selected="false" margin-right="8dp" />
|
||||||
|
<CheckBox id="escape_sequence" text='@string(use_escape_sequences, "Use escape sequences")' margin-right="8dp" selected="false"
|
||||||
|
tooltip='@string(escape_sequence_tooltip, "Replace \\, \t, \n, \r and \uXXXX (Unicode characters) with the corresponding control")' />
|
||||||
|
</StackLayout>
|
||||||
<hbox lw="mp" lh="wc">
|
<hbox lw="mp" lh="wc">
|
||||||
<TextView text='@string(history, "History:")' margin-right="4dp" lh="18dp" />
|
<TextView text='@string(history, "History:")' margin-right="4dp" lh="18dp" />
|
||||||
<DropDownList id="global_search_history" lw="0" lh="18dp" lw8="1" margin-right="4dp" />
|
<DropDownList id="global_search_history" lw="0" lh="18dp" lw8="1" margin-right="4dp" />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#include "ecode.hpp"
|
|
||||||
#include "globalsearchcontroller.hpp"
|
#include "globalsearchcontroller.hpp"
|
||||||
|
#include "ecode.hpp"
|
||||||
#include "uitreeviewglobalsearch.hpp"
|
#include "uitreeviewglobalsearch.hpp"
|
||||||
|
|
||||||
namespace ecode {
|
namespace ecode {
|
||||||
@@ -120,7 +120,7 @@ void GlobalSearchController::initGlobalSearchBar(
|
|||||||
mGlobalSearchBarLayout = globalSearchBar;
|
mGlobalSearchBarLayout = globalSearchBar;
|
||||||
mGlobalSearchBarLayout->setVisible( false )->setEnabled( false );
|
mGlobalSearchBarLayout->setVisible( false )->setEnabled( false );
|
||||||
auto addClickListener = [this]( UIWidget* widget, std::string cmd ) {
|
auto addClickListener = [this]( UIWidget* widget, std::string cmd ) {
|
||||||
widget->addEventListener( Event::MouseClick, [this, cmd]( const Event* event ) {
|
widget->on( Event::MouseClick, [this, cmd]( const Event* event ) {
|
||||||
const MouseEvent* mouseEvent = static_cast<const MouseEvent*>( event );
|
const MouseEvent* mouseEvent = static_cast<const MouseEvent*>( event );
|
||||||
if ( mouseEvent->getFlags() & EE_BUTTON_LMASK )
|
if ( mouseEvent->getFlags() & EE_BUTTON_LMASK )
|
||||||
mGlobalSearchBarLayout->execute( cmd );
|
mGlobalSearchBarLayout->execute( cmd );
|
||||||
@@ -165,33 +165,34 @@ void GlobalSearchController::initGlobalSearchBar(
|
|||||||
escapeSequenceChk->setChecked( globalSearchBarConfig.escapeSequence );
|
escapeSequenceChk->setChecked( globalSearchBarConfig.escapeSequence );
|
||||||
|
|
||||||
mGlobalSearchInput = mGlobalSearchBarLayout->find<UITextInput>( "global_search_find" );
|
mGlobalSearchInput = mGlobalSearchBarLayout->find<UITextInput>( "global_search_find" );
|
||||||
|
mGlobalSearchWhereInput = mGlobalSearchBarLayout->find<UITextInput>( "global_search_where" );
|
||||||
|
|
||||||
mGlobalSearchHistoryList =
|
mGlobalSearchHistoryList =
|
||||||
mGlobalSearchBarLayout->find<UIDropDownList>( "global_search_history" );
|
mGlobalSearchBarLayout->find<UIDropDownList>( "global_search_history" );
|
||||||
mGlobalSearchBarLayout->setCommand( "global-search-clear-history", [this] { clearHistory(); } );
|
mGlobalSearchBarLayout->setCommand( "global-search-clear-history", [this] { clearHistory(); } );
|
||||||
mGlobalSearchBarLayout->setCommand( "search-in-files", [this, caseSensitiveChk, wholeWordChk,
|
mGlobalSearchBarLayout->setCommand( "search-in-files", [this, caseSensitiveChk, wholeWordChk,
|
||||||
luaPatternChk, escapeSequenceChk] {
|
luaPatternChk, escapeSequenceChk] {
|
||||||
doGlobalSearch( mGlobalSearchInput->getText(), caseSensitiveChk->isChecked(),
|
doGlobalSearch( mGlobalSearchInput->getText(), mGlobalSearchWhereInput->getText(),
|
||||||
wholeWordChk->isChecked(), luaPatternChk->isChecked(),
|
caseSensitiveChk->isChecked(), wholeWordChk->isChecked(),
|
||||||
escapeSequenceChk->isChecked(), false );
|
luaPatternChk->isChecked(), escapeSequenceChk->isChecked(), false );
|
||||||
} );
|
} );
|
||||||
mGlobalSearchBarLayout->setCommand(
|
mGlobalSearchBarLayout->setCommand(
|
||||||
"search-again", [this, caseSensitiveChk, wholeWordChk, luaPatternChk, escapeSequenceChk] {
|
"search-again", [this, caseSensitiveChk, wholeWordChk, luaPatternChk, escapeSequenceChk] {
|
||||||
auto listBox = mGlobalSearchHistoryList->getListBox();
|
auto listBox = mGlobalSearchHistoryList->getListBox();
|
||||||
if ( listBox->getItemSelectedIndex() < mGlobalSearchHistory.size() ) {
|
if ( listBox->getItemSelectedIndex() < mGlobalSearchHistory.size() ) {
|
||||||
doGlobalSearch( mGlobalSearchHistory[mGlobalSearchHistory.size() - 1 -
|
const auto& item = mGlobalSearchHistory[mGlobalSearchHistory.size() - 1 -
|
||||||
listBox->getItemSelectedIndex()]
|
listBox->getItemSelectedIndex()];
|
||||||
.first,
|
doGlobalSearch( item.search, item.filter, caseSensitiveChk->isChecked(),
|
||||||
caseSensitiveChk->isChecked(), wholeWordChk->isChecked(),
|
wholeWordChk->isChecked(), luaPatternChk->isChecked(),
|
||||||
luaPatternChk->isChecked(), escapeSequenceChk->isChecked(),
|
escapeSequenceChk->isChecked(),
|
||||||
mGlobalSearchTreeReplace == mGlobalSearchTree, true );
|
mGlobalSearchTreeReplace == mGlobalSearchTree, true );
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
mGlobalSearchBarLayout->setCommand( "search-set-string", [this] {
|
mGlobalSearchBarLayout->setCommand( "search-set-string", [this] {
|
||||||
auto listBox = mGlobalSearchHistoryList->getListBox();
|
auto listBox = mGlobalSearchHistoryList->getListBox();
|
||||||
mGlobalSearchInput->setText(
|
const auto& item =
|
||||||
mGlobalSearchHistory[mGlobalSearchHistory.size() - 1 - listBox->getItemSelectedIndex()]
|
mGlobalSearchHistory[mGlobalSearchHistory.size() - 1 - listBox->getItemSelectedIndex()];
|
||||||
.first );
|
mGlobalSearchInput->setText( item.search );
|
||||||
;
|
;
|
||||||
} );
|
} );
|
||||||
mGlobalSearchBarLayout->setCommand( "close-global-searchbar", [this] {
|
mGlobalSearchBarLayout->setCommand( "close-global-searchbar", [this] {
|
||||||
@@ -220,15 +221,17 @@ void GlobalSearchController::initGlobalSearchBar(
|
|||||||
escapeSequenceChk->setChecked( !escapeSequenceChk->isChecked() );
|
escapeSequenceChk->setChecked( !escapeSequenceChk->isChecked() );
|
||||||
} );
|
} );
|
||||||
mGlobalSearchBarLayout->setCommand( "find-replace", [this] { mApp->showFindView(); } );
|
mGlobalSearchBarLayout->setCommand( "find-replace", [this] { mApp->showFindView(); } );
|
||||||
mGlobalSearchInput->addEventListener( Event::OnPressEnter, [this]( const Event* ) {
|
const auto pressEnterCb = [this]( const Event* event ) {
|
||||||
if ( mGlobalSearchInput->hasFocus() ) {
|
if ( event->getNode()->hasFocus() ) {
|
||||||
mGlobalSearchBarLayout->execute( "search-in-files" );
|
mGlobalSearchBarLayout->execute( "search-in-files" );
|
||||||
} else {
|
} else {
|
||||||
KeyEvent keyEvent( mGlobalSearchTree, Event::KeyDown, KEY_RETURN, SCANCODE_UNKNOWN, 0,
|
KeyEvent keyEvent( mGlobalSearchTree, Event::KeyDown, KEY_RETURN, SCANCODE_UNKNOWN, 0,
|
||||||
0 );
|
0 );
|
||||||
mGlobalSearchTree->forceKeyDown( keyEvent );
|
mGlobalSearchTree->forceKeyDown( keyEvent );
|
||||||
}
|
}
|
||||||
} );
|
};
|
||||||
|
mGlobalSearchInput->on( Event::OnPressEnter, pressEnterCb );
|
||||||
|
mGlobalSearchWhereInput->on( Event::OnPressEnter, pressEnterCb );
|
||||||
auto switchInputToTree = [this]( const Event* event ) {
|
auto switchInputToTree = [this]( const Event* event ) {
|
||||||
const KeyEvent* keyEvent = static_cast<const KeyEvent*>( event );
|
const KeyEvent* keyEvent = static_cast<const KeyEvent*>( event );
|
||||||
Uint32 keyCode = keyEvent->getKeyCode();
|
Uint32 keyCode = keyEvent->getKeyCode();
|
||||||
@@ -238,8 +241,8 @@ void GlobalSearchController::initGlobalSearchBar(
|
|||||||
mGlobalSearchTree->setFocus();
|
mGlobalSearchTree->setFocus();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
mGlobalSearchInput->addEventListener( Event::KeyDown, switchInputToTree );
|
mGlobalSearchInput->on( Event::KeyDown, switchInputToTree );
|
||||||
mGlobalSearchInput->addEventListener( Event::OnSizeChange, [this]( const Event* ) {
|
mGlobalSearchInput->on( Event::OnSizeChange, [this]( const Event* ) {
|
||||||
if ( mGlobalSearchBarLayout->isVisible() )
|
if ( mGlobalSearchBarLayout->isVisible() )
|
||||||
updateGlobalSearchBar();
|
updateGlobalSearchBar();
|
||||||
} );
|
} );
|
||||||
@@ -292,12 +295,12 @@ void GlobalSearchController::initGlobalSearchBar(
|
|||||||
addClickListener( replaceButton, "replace-in-files" );
|
addClickListener( replaceButton, "replace-in-files" );
|
||||||
addClickListener( searchExpandButton, "expand-all" );
|
addClickListener( searchExpandButton, "expand-all" );
|
||||||
addClickListener( searchCollapseButton, "collapse-all" );
|
addClickListener( searchCollapseButton, "collapse-all" );
|
||||||
replaceInput->addEventListener( Event::OnPressEnter, [this, replaceInput]( const Event* ) {
|
replaceInput->on( Event::OnPressEnter, [this, replaceInput]( const Event* ) {
|
||||||
if ( replaceInput->hasFocus() )
|
if ( replaceInput->hasFocus() )
|
||||||
mGlobalSearchBarLayout->execute( "replace-in-files" );
|
mGlobalSearchBarLayout->execute( "replace-in-files" );
|
||||||
} );
|
} );
|
||||||
replaceInput->addEventListener( Event::KeyDown, switchInputToTree );
|
replaceInput->on( Event::KeyDown, switchInputToTree );
|
||||||
mGlobalSearchLayout->addEventListener( Event::KeyDown, [this]( const Event* event ) {
|
mGlobalSearchLayout->on( Event::KeyDown, [this]( const Event* event ) {
|
||||||
const KeyEvent* keyEvent = static_cast<const KeyEvent*>( event );
|
const KeyEvent* keyEvent = static_cast<const KeyEvent*>( event );
|
||||||
if ( keyEvent->getKeyCode() == KEY_ESCAPE ) {
|
if ( keyEvent->getKeyCode() == KEY_ESCAPE ) {
|
||||||
mGlobalSearchBarLayout->execute( "close-global-searchbar" );
|
mGlobalSearchBarLayout->execute( "close-global-searchbar" );
|
||||||
@@ -322,9 +325,9 @@ void GlobalSearchController::initGlobalSearchBar(
|
|||||||
mGlobalSearchHistory.back().second, true, false );
|
mGlobalSearchHistory.back().second, true, false );
|
||||||
} else*/
|
} else*/
|
||||||
{
|
{
|
||||||
doGlobalSearch( mGlobalSearchInput->getText(), caseSensitiveChk->isChecked(),
|
doGlobalSearch( mGlobalSearchInput->getText(), mGlobalSearchWhereInput->getText(),
|
||||||
wholeWordChk->isChecked(), luaPatternChk->isChecked(),
|
caseSensitiveChk->isChecked(), wholeWordChk->isChecked(),
|
||||||
escapeSequenceChk->isChecked(), true );
|
luaPatternChk->isChecked(), escapeSequenceChk->isChecked(), true );
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
mGlobalSearchBarLayout->setCommand(
|
mGlobalSearchBarLayout->setCommand(
|
||||||
@@ -336,7 +339,7 @@ void GlobalSearchController::initGlobalSearchBar(
|
|||||||
String text( replaceInput->getText() );
|
String text( replaceInput->getText() );
|
||||||
if ( escapeSequenceChk->isChecked() )
|
if ( escapeSequenceChk->isChecked() )
|
||||||
text.unescape();
|
text.unescape();
|
||||||
size_t count = replaceInFiles( text.toUtf8(), replaceData.second );
|
size_t count = replaceInFiles( text.toUtf8(), replaceData.result );
|
||||||
mGlobalSearchBarLayout->execute( "search-again" );
|
mGlobalSearchBarLayout->execute( "search-again" );
|
||||||
mGlobalSearchBarLayout->execute( "close-global-searchbar" );
|
mGlobalSearchBarLayout->execute( "close-global-searchbar" );
|
||||||
mApp->getNotificationCenter()->addNotification(
|
mApp->getNotificationCenter()->addNotification(
|
||||||
@@ -349,7 +352,7 @@ void GlobalSearchController::initGlobalSearchBar(
|
|||||||
UITreeViewGlobalSearch::New( mSplitter->getCurrentColorScheme(), true );
|
UITreeViewGlobalSearch::New( mSplitter->getCurrentColorScheme(), true );
|
||||||
initGlobalSearchTree( mGlobalSearchTreeSearch );
|
initGlobalSearchTree( mGlobalSearchTreeSearch );
|
||||||
initGlobalSearchTree( mGlobalSearchTreeReplace );
|
initGlobalSearchTree( mGlobalSearchTreeReplace );
|
||||||
mGlobalSearchTreeReplace->addEventListener( Event::KeyDown, [this]( const Event* event ) {
|
mGlobalSearchTreeReplace->on( Event::KeyDown, [this]( const Event* event ) {
|
||||||
const KeyEvent* keyEvent = static_cast<const KeyEvent*>( event );
|
const KeyEvent* keyEvent = static_cast<const KeyEvent*>( event );
|
||||||
if ( keyEvent->getSanitizedMod() == KeyMod::getDefaultModifier() &&
|
if ( keyEvent->getSanitizedMod() == KeyMod::getDefaultModifier() &&
|
||||||
keyEvent->getKeyCode() == KEY_RETURN )
|
keyEvent->getKeyCode() == KEY_RETURN )
|
||||||
@@ -416,6 +419,7 @@ void GlobalSearchController::clearHistory() {
|
|||||||
mGlobalSearchTree->setSearchStr( "" );
|
mGlobalSearchTree->setSearchStr( "" );
|
||||||
mGlobalSearchTree->setModel( nullptr );
|
mGlobalSearchTree->setModel( nullptr );
|
||||||
mGlobalSearchInput->setText( "" );
|
mGlobalSearchInput->setText( "" );
|
||||||
|
mGlobalSearchWhereInput->setText( "" );
|
||||||
mGlobalSearchLayout->findByClass( "status_box" )->setVisible( false );
|
mGlobalSearchLayout->findByClass( "status_box" )->setVisible( false );
|
||||||
mGlobalSearchLayout->findByClass<UITextView>( "search_str" )->setText( "" );
|
mGlobalSearchLayout->findByClass<UITextView>( "search_str" )->setText( "" );
|
||||||
updateGlobalSearchBar();
|
updateGlobalSearchBar();
|
||||||
@@ -494,18 +498,18 @@ void GlobalSearchController::updateGlobalSearchBarResults(
|
|||||||
|
|
||||||
void GlobalSearchController::updateGlobalSearchHistory(
|
void GlobalSearchController::updateGlobalSearchHistory(
|
||||||
std::shared_ptr<ProjectSearch::ResultModel> model, const std::string& search,
|
std::shared_ptr<ProjectSearch::ResultModel> model, const std::string& search,
|
||||||
bool searchReplace, bool searchAgain, bool escapeSequence ) {
|
const std::string& filter, bool searchReplace, bool searchAgain, bool escapeSequence ) {
|
||||||
auto listBox = mGlobalSearchHistoryList->getListBox();
|
auto listBox = mGlobalSearchHistoryList->getListBox();
|
||||||
|
|
||||||
if ( !searchAgain ) {
|
if ( !searchAgain ) {
|
||||||
mGlobalSearchHistory.push_back( std::make_pair( search, model ) );
|
mGlobalSearchHistory.push_back( { search, filter, model } );
|
||||||
if ( mGlobalSearchHistory.size() > 10 )
|
if ( mGlobalSearchHistory.size() > 10 )
|
||||||
mGlobalSearchHistory.pop_front();
|
mGlobalSearchHistory.pop_front();
|
||||||
|
|
||||||
std::vector<String> items;
|
std::vector<String> items;
|
||||||
for ( auto item = mGlobalSearchHistory.rbegin(); item != mGlobalSearchHistory.rend();
|
for ( auto item = mGlobalSearchHistory.rbegin(); item != mGlobalSearchHistory.rend();
|
||||||
item++ ) {
|
item++ ) {
|
||||||
items.push_back( item->first );
|
items.push_back( item->search );
|
||||||
}
|
}
|
||||||
|
|
||||||
listBox->clear();
|
listBox->clear();
|
||||||
@@ -513,21 +517,35 @@ void GlobalSearchController::updateGlobalSearchHistory(
|
|||||||
if ( mGlobalSearchHistoryOnItemSelectedCb )
|
if ( mGlobalSearchHistoryOnItemSelectedCb )
|
||||||
mGlobalSearchHistoryList->removeEventListener( mGlobalSearchHistoryOnItemSelectedCb );
|
mGlobalSearchHistoryList->removeEventListener( mGlobalSearchHistoryOnItemSelectedCb );
|
||||||
listBox->setSelected( 0 );
|
listBox->setSelected( 0 );
|
||||||
mGlobalSearchHistoryOnItemSelectedCb = mGlobalSearchHistoryList->addEventListener(
|
mGlobalSearchHistoryOnItemSelectedCb = mGlobalSearchHistoryList->on(
|
||||||
Event::OnItemSelected, [this, escapeSequence, searchReplace]( const Event* ) {
|
Event::OnItemSelected, [this, escapeSequence, searchReplace]( const Event* ) {
|
||||||
auto idx = mGlobalSearchHistoryList->getListBox()->getItemSelectedIndex();
|
auto idx = mGlobalSearchHistoryList->getListBox()->getItemSelectedIndex();
|
||||||
auto idxItem = mGlobalSearchHistory.at( mGlobalSearchHistory.size() - 1 - idx );
|
auto idxItem = mGlobalSearchHistory.at( mGlobalSearchHistory.size() - 1 - idx );
|
||||||
updateGlobalSearchBarResults( idxItem.first, idxItem.second, searchReplace,
|
updateGlobalSearchBarResults( idxItem.search, idxItem.result, searchReplace,
|
||||||
escapeSequence );
|
escapeSequence );
|
||||||
} );
|
} );
|
||||||
} else if ( listBox->getItemSelectedIndex() < mGlobalSearchHistory.size() ) {
|
} else if ( listBox->getItemSelectedIndex() < mGlobalSearchHistory.size() ) {
|
||||||
mGlobalSearchHistory[mGlobalSearchHistory.size() - 1 - listBox->getItemSelectedIndex()]
|
mGlobalSearchHistory[mGlobalSearchHistory.size() - 1 - listBox->getItemSelectedIndex()]
|
||||||
.second = model;
|
.result = model;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlobalSearchController::doGlobalSearch( String text, bool caseSensitive, bool wholeWord,
|
std::vector<GlobMatch> GlobalSearchController::parseGlobMatches( const String& str ) {
|
||||||
bool luaPattern, bool escapeSequence,
|
std::vector<GlobMatch> ret;
|
||||||
|
auto globs = str.split( ',' );
|
||||||
|
for ( auto& glob : globs ) {
|
||||||
|
String::trimInPlace( glob );
|
||||||
|
if ( !glob.empty() && glob[0] == '-' ) {
|
||||||
|
ret.push_back( { glob.substr( 1 ), true } );
|
||||||
|
} else {
|
||||||
|
ret.push_back( { glob, false } );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalSearchController::doGlobalSearch( String text, String filter, bool caseSensitive,
|
||||||
|
bool wholeWord, bool luaPattern, bool escapeSequence,
|
||||||
bool searchReplace, bool searchAgain ) {
|
bool searchReplace, bool searchAgain ) {
|
||||||
if ( mApp->getDirTree() && mApp->getDirTree()->getFilesCount() > 0 && !text.empty() ) {
|
if ( mApp->getDirTree() && mApp->getDirTree()->getFilesCount() > 0 && !text.empty() ) {
|
||||||
mGlobalSearchTree = searchReplace ? mGlobalSearchTreeReplace : mGlobalSearchTreeSearch;
|
mGlobalSearchTree = searchReplace ? mGlobalSearchTreeReplace : mGlobalSearchTreeSearch;
|
||||||
@@ -552,16 +570,16 @@ void GlobalSearchController::doGlobalSearch( String text, bool caseSensitive, bo
|
|||||||
#if EE_PLATFORM != EE_PLATFORM_EMSCRIPTEN || defined( __EMSCRIPTEN_PTHREADS__ )
|
#if EE_PLATFORM != EE_PLATFORM_EMSCRIPTEN || defined( __EMSCRIPTEN_PTHREADS__ )
|
||||||
mApp->getThreadPool(),
|
mApp->getThreadPool(),
|
||||||
#endif
|
#endif
|
||||||
[this, clock, search, loader, searchReplace, searchAgain, escapeSequence,
|
[this, clock, search, loader, searchReplace, searchAgain, escapeSequence, luaPattern,
|
||||||
luaPattern]( const ProjectSearch::Result& res ) {
|
filter]( const ProjectSearch::Result& res ) {
|
||||||
Log::info( "Global search for \"%s\" took %.2fms", search.c_str(),
|
Log::info( "Global search for \"%s\" took %.2fms", search.c_str(),
|
||||||
clock->getElapsedTime().asMilliseconds() );
|
clock->getElapsedTime().asMilliseconds() );
|
||||||
eeDelete( clock );
|
eeDelete( clock );
|
||||||
mUISceneNode->runOnMainThread( [this, loader, res, search, searchReplace,
|
mUISceneNode->runOnMainThread( [this, loader, res, search, searchReplace,
|
||||||
searchAgain, escapeSequence, luaPattern] {
|
searchAgain, escapeSequence, luaPattern, filter] {
|
||||||
auto model = ProjectSearch::asModel( res );
|
auto model = ProjectSearch::asModel( res );
|
||||||
model->setResultFromLuaPattern( luaPattern );
|
model->setResultFromLuaPattern( luaPattern );
|
||||||
updateGlobalSearchHistory( model, search, searchReplace, searchAgain,
|
updateGlobalSearchHistory( model, search, filter, searchReplace, searchAgain,
|
||||||
escapeSequence );
|
escapeSequence );
|
||||||
updateGlobalSearchBarResults( search, model, searchReplace, escapeSequence );
|
updateGlobalSearchBarResults( search, model, searchReplace, escapeSequence );
|
||||||
loader->setVisible( false );
|
loader->setVisible( false );
|
||||||
@@ -570,7 +588,8 @@ void GlobalSearchController::doGlobalSearch( String text, bool caseSensitive, bo
|
|||||||
},
|
},
|
||||||
caseSensitive, wholeWord,
|
caseSensitive, wholeWord,
|
||||||
luaPattern ? TextDocument::FindReplaceType::LuaPattern
|
luaPattern ? TextDocument::FindReplaceType::LuaPattern
|
||||||
: TextDocument::FindReplaceType::Normal );
|
: TextDocument::FindReplaceType::Normal,
|
||||||
|
parseGlobMatches( filter ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -594,7 +613,7 @@ void GlobalSearchController::initGlobalSearchTree( UITreeViewGlobalSearch* searc
|
|||||||
searchTree->setHeadersVisible( false );
|
searchTree->setHeadersVisible( false );
|
||||||
searchTree->setColumnsHidden(
|
searchTree->setColumnsHidden(
|
||||||
{ ProjectSearch::ResultModel::Line, ProjectSearch::ResultModel::ColumnStart }, true );
|
{ ProjectSearch::ResultModel::Line, ProjectSearch::ResultModel::ColumnStart }, true );
|
||||||
searchTree->addEventListener( Event::OnModelEvent, [this]( const Event* event ) {
|
searchTree->on( Event::OnModelEvent, [this]( const Event* event ) {
|
||||||
const ModelEvent* modelEvent = static_cast<const ModelEvent*>( event );
|
const ModelEvent* modelEvent = static_cast<const ModelEvent*>( event );
|
||||||
if ( modelEvent->getModelEventType() == ModelEventType::Open ) {
|
if ( modelEvent->getModelEventType() == ModelEventType::Open ) {
|
||||||
const Model* model = modelEvent->getModel();
|
const Model* model = modelEvent->getModel();
|
||||||
@@ -678,7 +697,8 @@ PluginRequestHandle GlobalSearchController::processMessage( const PluginMessage&
|
|||||||
mUISceneNode->runOnMainThread( [this, search, model] {
|
mUISceneNode->runOnMainThread( [this, search, model] {
|
||||||
showGlobalSearch( false );
|
showGlobalSearch( false );
|
||||||
mGlobalSearchInput->setText( search );
|
mGlobalSearchInput->setText( search );
|
||||||
updateGlobalSearchHistory( model, search, false, false, false );
|
mGlobalSearchWhereInput->setText( "" );
|
||||||
|
updateGlobalSearchHistory( model, search, "", false, false, false );
|
||||||
updateGlobalSearchBarResults( search, model, false, false );
|
updateGlobalSearchBarResults( search, model, false, false );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
|||||||
@@ -47,8 +47,9 @@ class GlobalSearchController {
|
|||||||
|
|
||||||
void initGlobalSearchTree( UITreeViewGlobalSearch* searchTree );
|
void initGlobalSearchTree( UITreeViewGlobalSearch* searchTree );
|
||||||
|
|
||||||
void doGlobalSearch( String text, bool caseSensitive, bool wholeWord, bool luaPattern,
|
void doGlobalSearch( String text, String filter, bool caseSensitive, bool wholeWord,
|
||||||
bool escapeSequence, bool searchReplace, bool searchAgain = false );
|
bool luaPattern, bool escapeSequence, bool searchReplace,
|
||||||
|
bool searchAgain = false );
|
||||||
|
|
||||||
size_t replaceInFiles( const std::string& replaceText,
|
size_t replaceInFiles( const std::string& replaceText,
|
||||||
std::shared_ptr<ProjectSearch::ResultModel> model );
|
std::shared_ptr<ProjectSearch::ResultModel> model );
|
||||||
@@ -76,18 +77,25 @@ class GlobalSearchController {
|
|||||||
UITreeViewGlobalSearch* mGlobalSearchTreeSearch{ nullptr };
|
UITreeViewGlobalSearch* mGlobalSearchTreeSearch{ nullptr };
|
||||||
UITreeViewGlobalSearch* mGlobalSearchTreeReplace{ nullptr };
|
UITreeViewGlobalSearch* mGlobalSearchTreeReplace{ nullptr };
|
||||||
UITextInput* mGlobalSearchInput{ nullptr };
|
UITextInput* mGlobalSearchInput{ nullptr };
|
||||||
|
UITextInput* mGlobalSearchWhereInput{ nullptr };
|
||||||
UIDropDownList* mGlobalSearchHistoryList{ nullptr };
|
UIDropDownList* mGlobalSearchHistoryList{ nullptr };
|
||||||
Uint32 mGlobalSearchHistoryOnItemSelectedCb{ 0 };
|
Uint32 mGlobalSearchHistoryOnItemSelectedCb{ 0 };
|
||||||
std::deque<std::pair<std::string, std::shared_ptr<ProjectSearch::ResultModel>>>
|
struct SearchHistoryItem {
|
||||||
mGlobalSearchHistory;
|
std::string search;
|
||||||
|
std::string filter;
|
||||||
|
std::shared_ptr<ProjectSearch::ResultModel> result;
|
||||||
|
};
|
||||||
|
std::deque<SearchHistoryItem> mGlobalSearchHistory;
|
||||||
|
|
||||||
void onLoadDone( const Variant& lineNum, const Variant& colNum );
|
void onLoadDone( const Variant& lineNum, const Variant& colNum );
|
||||||
|
|
||||||
PluginRequestHandle processMessage( const PluginMessage& msg );
|
PluginRequestHandle processMessage( const PluginMessage& msg );
|
||||||
|
|
||||||
void updateGlobalSearchHistory( std::shared_ptr<ProjectSearch::ResultModel> model,
|
void updateGlobalSearchHistory( std::shared_ptr<ProjectSearch::ResultModel> model,
|
||||||
const std::string& search, bool searchReplace, bool searchAgain,
|
const std::string& search, const std::string& filter,
|
||||||
bool escapeSequence );
|
bool searchReplace, bool searchAgain, bool escapeSequence );
|
||||||
|
|
||||||
|
std::vector<GlobMatch> parseGlobMatches( const String& str );
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ecode
|
} // namespace ecode
|
||||||
|
|||||||
@@ -5,152 +5,6 @@
|
|||||||
|
|
||||||
namespace ecode {
|
namespace ecode {
|
||||||
|
|
||||||
// Author: Robert A. van Engelen, engelen@genivia.com
|
|
||||||
// Date: August 5, 2019
|
|
||||||
// License: The Code Project Open License (CPOL)
|
|
||||||
// https://www.codeproject.com/info/cpol10.aspx
|
|
||||||
|
|
||||||
#define DOTGLOB 1
|
|
||||||
#define PATHSEP '/'
|
|
||||||
#define CASE( c, caseInsensitive ) ( caseInsensitive ? std::tolower( c ) : ( c ) )
|
|
||||||
|
|
||||||
bool IgnoreMatcher::globMatch( const std::string& text, const std::string_view& glob,
|
|
||||||
bool caseInsensitive ) {
|
|
||||||
size_t i = 0;
|
|
||||||
size_t j = 0;
|
|
||||||
size_t n = text.size();
|
|
||||||
size_t m = glob.size();
|
|
||||||
size_t text1_backup = std::string::npos;
|
|
||||||
size_t glob1_backup = std::string::npos;
|
|
||||||
size_t text2_backup = std::string::npos;
|
|
||||||
size_t glob2_backup = std::string::npos;
|
|
||||||
bool nodot = !DOTGLOB;
|
|
||||||
// match pathname if glob contains a / otherwise match the basename
|
|
||||||
if ( j + 1 < m && glob[j] == '/' ) {
|
|
||||||
// if pathname starts with ./ then ignore these pairs
|
|
||||||
while ( i + 1 < n && text[i] == '.' && text[i + 1] == PATHSEP )
|
|
||||||
i += 2;
|
|
||||||
// if pathname starts with a / then ignore it
|
|
||||||
if ( i < n && text[i] == PATHSEP )
|
|
||||||
i++;
|
|
||||||
j++;
|
|
||||||
} else if ( glob.find( '/' ) == std::string::npos ) {
|
|
||||||
size_t sep = text.rfind( PATHSEP );
|
|
||||||
if ( sep != std::string::npos )
|
|
||||||
i = sep + 1;
|
|
||||||
}
|
|
||||||
while ( i < n ) {
|
|
||||||
if ( j < m ) {
|
|
||||||
switch ( glob[j] ) {
|
|
||||||
case '*':
|
|
||||||
// match anything except . after /
|
|
||||||
if ( nodot && text[i] == '.' )
|
|
||||||
break;
|
|
||||||
if ( ++j < m && glob[j] == '*' ) {
|
|
||||||
// trailing ** match everything after /
|
|
||||||
if ( ++j >= m )
|
|
||||||
return true;
|
|
||||||
// ** followed by a / match zero or more directories
|
|
||||||
if ( glob[j] != '/' )
|
|
||||||
return false;
|
|
||||||
// new **-loop, discard *-loop
|
|
||||||
text1_backup = std::string::npos;
|
|
||||||
glob1_backup = std::string::npos;
|
|
||||||
text2_backup = i;
|
|
||||||
glob2_backup = ++j;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// trailing * matches everything except /
|
|
||||||
text1_backup = i;
|
|
||||||
glob1_backup = j;
|
|
||||||
continue;
|
|
||||||
case '?':
|
|
||||||
// match anything except . after /
|
|
||||||
if ( nodot && text[i] == '.' )
|
|
||||||
break;
|
|
||||||
// match any character except /
|
|
||||||
if ( text[i] == PATHSEP )
|
|
||||||
break;
|
|
||||||
i++;
|
|
||||||
j++;
|
|
||||||
continue;
|
|
||||||
case '[': {
|
|
||||||
// match anything except . after /
|
|
||||||
if ( nodot && text[i] == '.' )
|
|
||||||
break;
|
|
||||||
// match any character in [...] except /
|
|
||||||
if ( text[i] == PATHSEP )
|
|
||||||
break;
|
|
||||||
int lastchr;
|
|
||||||
bool matched = false;
|
|
||||||
bool reverse = j + 1 < m && ( glob[j + 1] == '^' || glob[j + 1] == '!' );
|
|
||||||
// inverted character class
|
|
||||||
if ( reverse )
|
|
||||||
j++;
|
|
||||||
// match character class
|
|
||||||
for ( lastchr = 256; ++j < m && glob[j] != ']';
|
|
||||||
lastchr = CASE( glob[j], caseInsensitive ) )
|
|
||||||
if ( lastchr < 256 && glob[j] == '-' && j + 1 < m && glob[j + 1] != ']'
|
|
||||||
? CASE( text[i], caseInsensitive ) <=
|
|
||||||
CASE( glob[++j], caseInsensitive ) &&
|
|
||||||
CASE( text[i], caseInsensitive ) >= lastchr
|
|
||||||
: CASE( text[i], caseInsensitive ) ==
|
|
||||||
CASE( glob[j], caseInsensitive ) )
|
|
||||||
matched = true;
|
|
||||||
if ( matched == reverse )
|
|
||||||
break;
|
|
||||||
i++;
|
|
||||||
if ( j < m )
|
|
||||||
j++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
case '\\':
|
|
||||||
// literal match \-escaped character
|
|
||||||
if ( j + 1 < m )
|
|
||||||
j++;
|
|
||||||
// FALLTHROUGH
|
|
||||||
default:
|
|
||||||
// match the current non-NUL character
|
|
||||||
if ( CASE( glob[j], caseInsensitive ) != CASE( text[i], caseInsensitive ) &&
|
|
||||||
!( glob[j] == '/' && text[i] == PATHSEP ) )
|
|
||||||
break;
|
|
||||||
// do not match a . with *, ? [] after /
|
|
||||||
nodot = !DOTGLOB && glob[j] == '/';
|
|
||||||
i++;
|
|
||||||
j++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( glob1_backup != std::string::npos && text[text1_backup] != PATHSEP ) {
|
|
||||||
// *-loop: backtrack to the last * but do not jump over /
|
|
||||||
i = ++text1_backup;
|
|
||||||
j = glob1_backup;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ( glob2_backup != std::string::npos ) {
|
|
||||||
// **-loop: backtrack to the last **
|
|
||||||
i = ++text2_backup;
|
|
||||||
j = glob2_backup;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// ignore trailing stars
|
|
||||||
while ( j < m && glob[j] == '*' )
|
|
||||||
j++;
|
|
||||||
// at end of text means success if nothing else is left to match
|
|
||||||
return j >= m;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IgnoreMatcher::globMatch( const std::string& text, const std::vector<std::string>& globs,
|
|
||||||
bool caseInsensitive ) {
|
|
||||||
for ( const auto& glob : globs ) {
|
|
||||||
if ( globMatch( text, glob, caseInsensitive ) )
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
IgnoreMatcher::IgnoreMatcher( const std::string& rootPath ) : mPath( rootPath ) {
|
IgnoreMatcher::IgnoreMatcher( const std::string& rootPath ) : mPath( rootPath ) {
|
||||||
FileSystem::dirAddSlashAtEnd( mPath );
|
FileSystem::dirAddSlashAtEnd( mPath );
|
||||||
}
|
}
|
||||||
@@ -207,7 +61,7 @@ bool GitIgnoreMatcher::match( const std::string& value ) const {
|
|||||||
bool match = false;
|
bool match = false;
|
||||||
for ( size_t i = 0; i < mPatterns.size(); i++ ) {
|
for ( size_t i = 0; i < mPatterns.size(); i++ ) {
|
||||||
auto& pattern = mPatterns[i];
|
auto& pattern = mPatterns[i];
|
||||||
match = globMatch( value, pattern.first );
|
match = String::globMatch( value, pattern.first );
|
||||||
if ( pattern.second )
|
if ( pattern.second )
|
||||||
match = !match;
|
match = !match;
|
||||||
if ( match && !pattern.second ) {
|
if ( match && !pattern.second ) {
|
||||||
@@ -215,7 +69,7 @@ bool GitIgnoreMatcher::match( const std::string& value ) const {
|
|||||||
for ( size_t n = i + 1; n < mPatterns.size(); n++ ) {
|
for ( size_t n = i + 1; n < mPatterns.size(); n++ ) {
|
||||||
// Check if there's a positive negate after the match
|
// Check if there's a positive negate after the match
|
||||||
if ( mPatterns[n].second ) {
|
if ( mPatterns[n].second ) {
|
||||||
if ( globMatch( value, mPatterns[n].first ) )
|
if ( String::globMatch( value, mPatterns[n].first ) )
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -28,12 +28,6 @@ class IgnoreMatcher {
|
|||||||
|
|
||||||
const bool& matcherReady() const { return mMatcherReady; }
|
const bool& matcherReady() const { return mMatcherReady; }
|
||||||
|
|
||||||
static bool globMatch( const std::string& text, const std::string_view& glob,
|
|
||||||
bool caseInsensitive = false );
|
|
||||||
|
|
||||||
static bool globMatch( const std::string& text, const std::vector<std::string>& globs,
|
|
||||||
bool caseInsensitive = false );
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::string mPath;
|
std::string mPath;
|
||||||
bool mMatcherReady{ false };
|
bool mMatcherReady{ false };
|
||||||
|
|||||||
@@ -144,13 +144,25 @@ searchInFileLuaPattern( const std::string& file, const std::string& text, const
|
|||||||
|
|
||||||
void ProjectSearch::find( const std::vector<std::string> files, const std::string& string,
|
void ProjectSearch::find( const std::vector<std::string> files, const std::string& string,
|
||||||
ResultCb result, bool caseSensitive, bool wholeWord,
|
ResultCb result, bool caseSensitive, bool wholeWord,
|
||||||
const TextDocument::FindReplaceType& type ) {
|
const TextDocument::FindReplaceType& type,
|
||||||
|
const std::vector<GlobMatch>& pathFilters ) {
|
||||||
Result res;
|
Result res;
|
||||||
const auto occ =
|
const auto occ =
|
||||||
type == TextDocument::FindReplaceType::Normal
|
type == TextDocument::FindReplaceType::Normal
|
||||||
? String::BMH::createOccTable( (const unsigned char*)string.c_str(), string.size() )
|
? String::BMH::createOccTable( (const unsigned char*)string.c_str(), string.size() )
|
||||||
: std::vector<size_t>();
|
: std::vector<size_t>();
|
||||||
for ( auto& file : files ) {
|
for ( auto& file : files ) {
|
||||||
|
bool skip = false;
|
||||||
|
for ( const auto& filter : pathFilters ) {
|
||||||
|
bool matches = String::globMatch( file, filter.first );
|
||||||
|
if ( ( matches && filter.second ) || ( !matches && !filter.second ) ) {
|
||||||
|
skip = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( skip )
|
||||||
|
continue;
|
||||||
|
|
||||||
auto fileRes = type == TextDocument::FindReplaceType::Normal
|
auto fileRes = type == TextDocument::FindReplaceType::Normal
|
||||||
? searchInFileHorspool( file, string, caseSensitive, wholeWord, occ )
|
? searchInFileHorspool( file, string, caseSensitive, wholeWord, occ )
|
||||||
: searchInFileLuaPattern( file, string, caseSensitive, wholeWord );
|
: searchInFileLuaPattern( file, string, caseSensitive, wholeWord );
|
||||||
@@ -169,7 +181,8 @@ struct FindData {
|
|||||||
|
|
||||||
void ProjectSearch::find( const std::vector<std::string> files, std::string string,
|
void ProjectSearch::find( const std::vector<std::string> files, std::string string,
|
||||||
std::shared_ptr<ThreadPool> pool, ResultCb result, bool caseSensitive,
|
std::shared_ptr<ThreadPool> pool, ResultCb result, bool caseSensitive,
|
||||||
bool wholeWord, const TextDocument::FindReplaceType& type ) {
|
bool wholeWord, const TextDocument::FindReplaceType& type,
|
||||||
|
const std::vector<GlobMatch>& pathFilters ) {
|
||||||
if ( files.empty() )
|
if ( files.empty() )
|
||||||
result( {} );
|
result( {} );
|
||||||
FindData* findData = eeNew( FindData, () );
|
FindData* findData = eeNew( FindData, () );
|
||||||
@@ -180,7 +193,43 @@ void ProjectSearch::find( const std::vector<std::string> files, std::string stri
|
|||||||
type == TextDocument::FindReplaceType::Normal
|
type == TextDocument::FindReplaceType::Normal
|
||||||
? String::BMH::createOccTable( (const unsigned char*)string.c_str(), string.size() )
|
? String::BMH::createOccTable( (const unsigned char*)string.c_str(), string.size() )
|
||||||
: std::vector<size_t>();
|
: std::vector<size_t>();
|
||||||
|
std::vector<bool> search;
|
||||||
|
search.resize( files.size() );
|
||||||
|
size_t pos = 0;
|
||||||
|
size_t count = 0;
|
||||||
for ( auto& file : files ) {
|
for ( auto& file : files ) {
|
||||||
|
bool skip = false;
|
||||||
|
for ( const auto& filter : pathFilters ) {
|
||||||
|
bool matches = String::globMatch( file, filter.first );
|
||||||
|
if ( ( matches && filter.second ) || ( !matches && !filter.second ) ) {
|
||||||
|
skip = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( skip ) {
|
||||||
|
search[pos++] = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
search[pos++] = true;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
findData->resCount = count;
|
||||||
|
|
||||||
|
if ( count == 0 ) {
|
||||||
|
result( findData->res );
|
||||||
|
eeDelete( findData );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = 0;
|
||||||
|
for ( const auto& file : files ) {
|
||||||
|
if ( !search[pos] ) {
|
||||||
|
pos++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
|
||||||
pool->run(
|
pool->run(
|
||||||
[findData, file, string, caseSensitive, wholeWord, occ, type] {
|
[findData, file, string, caseSensitive, wholeWord, occ, type] {
|
||||||
auto fileRes =
|
auto fileRes =
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ using namespace EE::UI::Models;
|
|||||||
|
|
||||||
namespace ecode {
|
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 {
|
class ProjectSearch {
|
||||||
public:
|
public:
|
||||||
struct ResultData {
|
struct ResultData {
|
||||||
@@ -208,13 +211,15 @@ class ProjectSearch {
|
|||||||
static void
|
static void
|
||||||
find( const std::vector<std::string> files, const std::string& string, ResultCb result,
|
find( const std::vector<std::string> files, const std::string& string, ResultCb result,
|
||||||
bool caseSensitive, bool wholeWord = false,
|
bool caseSensitive, bool wholeWord = false,
|
||||||
const TextDocument::FindReplaceType& type = TextDocument::FindReplaceType::Normal );
|
const TextDocument::FindReplaceType& type = TextDocument::FindReplaceType::Normal,
|
||||||
|
const std::vector<GlobMatch>& pathFilters = {} );
|
||||||
|
|
||||||
static void
|
static void
|
||||||
find( const std::vector<std::string> files, std::string string,
|
find( const std::vector<std::string> files, std::string string,
|
||||||
std::shared_ptr<ThreadPool> pool, ResultCb result, bool caseSensitive,
|
std::shared_ptr<ThreadPool> pool, ResultCb result, bool caseSensitive,
|
||||||
bool wholeWord = false,
|
bool wholeWord = false,
|
||||||
const TextDocument::FindReplaceType& type = TextDocument::FindReplaceType::Normal );
|
const TextDocument::FindReplaceType& type = TextDocument::FindReplaceType::Normal,
|
||||||
|
const std::vector<GlobMatch>& pathFilters = {} );
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ecode
|
} // namespace ecode
|
||||||
|
|||||||
Reference in New Issue
Block a user