1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-10 04:22:20 +02:00

Improved performance of fuzzy string matching

This commit is contained in:
Simon Lübeß 2021-12-18 22:28:44 +01:00
parent b70745ef1e
commit 04888b8f10
4 changed files with 79 additions and 70 deletions

View file

@ -409,7 +409,7 @@ namespace IDE.ui
for(char32 c in mEntryDisplay.DecodedChars) for(char32 c in mEntryDisplay.DecodedChars)
loop: loop:
{ {
if(mMatchIndices.Contains((uint8)index)) if(mMatchIndices?.Contains((uint8)index) == true)
{ {
g.PushColor(DarkTheme.COLOR_MENU_FOCUSED); g.PushColor(DarkTheme.COLOR_MENU_FOCUSED);
defer:loop g.PopColor(); defer:loop g.PopColor();

View file

@ -30,20 +30,68 @@ AutoCompleteBase::~AutoCompleteBase()
AutoCompleteEntry* AutoCompleteBase::AddEntry(AutoCompleteEntry& entry, const StringImpl& filter) AutoCompleteEntry* AutoCompleteBase::AddEntry(AutoCompleteEntry& entry, const StringImpl& filter)
{ {
if ((!DoesFilterMatch(entry.mDisplay, filter.c_str(), entry.mScore, entry.mMatches, sizeof(entry.mMatches))) || (entry.mNamePrefixCount < 0)) uint8 matches[256];
if (!DoesFilterMatch(entry.mDisplay, filter.c_str(), entry.mScore, matches, 256) || (entry.mNamePrefixCount < 0))
return NULL; return NULL;
if (matches[0] != UINT8_MAX)
{
for (uint8 i = 0;; i++)
{
uint8 matchIndex = matches[i];
if ((matchIndex == 0 && i != 0) || i == UINT8_MAX)
{
entry.mMatchesLength = i;
break;
}
}
if (entry.mMatches != nullptr)
delete entry.mMatches;
entry.mMatches = new uint8[entry.mMatchesLength];
memcpy(entry.mMatches, matches, entry.mMatchesLength);
}
return AddEntry(entry); return AddEntry(entry);
} }
AutoCompleteEntry* AutoCompleteBase::AddEntry(AutoCompleteEntry& entry, const char* filter) AutoCompleteEntry* AutoCompleteBase::AddEntry(AutoCompleteEntry& entry, const char* filter)
{ {
if ((!DoesFilterMatch(entry.mDisplay, filter, entry.mScore, entry.mMatches, sizeof(entry.mMatches))) || (entry.mNamePrefixCount < 0)) uint8 matches[256];
if (!DoesFilterMatch(entry.mDisplay, filter, entry.mScore, matches, 256) || (entry.mNamePrefixCount < 0))
return NULL; return NULL;
if (matches[0] != UINT8_MAX)
{
for (uint8 i = 0;; i++)
{
uint8 matchIndex = matches[i];
if ((matchIndex == 0 && i != 0) || i == UINT8_MAX)
{
entry.mMatchesLength = i;
break;
}
}
if (entry.mMatches != nullptr)
delete entry.mMatches;
entry.mMatches = new uint8[entry.mMatchesLength];
memcpy(entry.mMatches, matches, entry.mMatchesLength);
}
return AddEntry(entry); return AddEntry(entry);
} }
AutoCompleteEntry* AutoCompleteBase::AddEntry(const AutoCompleteEntry& entry) AutoCompleteEntry* AutoCompleteBase::AddEntry(const AutoCompleteEntry& entry)
{ {
if (mEntriesSet.mAllocSize == 0) if (mEntriesSet.mAllocSize == 0)
{ {
mEntriesSet.Reserve(128); mEntriesSet.Reserve(128);
@ -58,13 +106,16 @@ AutoCompleteEntry* AutoCompleteBase::AddEntry(const AutoCompleteEntry& entry)
int size = (int)strlen(display) + 1; int size = (int)strlen(display) + 1;
insertedEntry->mDisplay = (char*)mAlloc.AllocBytes(size); insertedEntry->mDisplay = (char*)mAlloc.AllocBytes(size);
memcpy((char*)insertedEntry->mDisplay, display, size); memcpy((char*)insertedEntry->mDisplay, display, size);
insertedEntry->mMatches = (uint8*)mAlloc.AllocBytes(insertedEntry->mMatchesLength);
memcpy((char*)insertedEntry->mMatches, entry.mMatches, insertedEntry->mMatchesLength);
} }
return insertedEntry; return insertedEntry;
} }
bool AutoCompleteBase::DoesFilterMatch(const char* entry, const char* filter, int& score, uint8* matches, int maxMatches) bool AutoCompleteBase::DoesFilterMatch(const char* entry, const char* filter, int& score, uint8* matches, int maxMatches)
{ {
if (mIsGetDefinition) if (mIsGetDefinition)
{ {
int entryLen = (int)strlen(entry); int entryLen = (int)strlen(entry);
@ -95,57 +146,7 @@ bool AutoCompleteBase::DoesFilterMatch(const char* entry, const char* filter, in
return false; return false;
} }
// TODO: also do matches (but probably optimize them)
return fts::fuzzy_match(filter, entry, score, matches, maxMatches); return fts::fuzzy_match(filter, entry, score, matches, maxMatches);
/*
bool hasUnderscore = false;
bool checkInitials = filterLen > 1;
for (int i = 0; i < (int)filterLen; i++)
{
char c = filter[i];
if (c == '_')
hasUnderscore = true;
else if (islower((uint8)filter[i]))
checkInitials = false;
}
if (hasUnderscore)
return strnicmp(filter, entry, filterLen) == 0;
char initialStr[256];
char* initialStrP = initialStr;
//String initialStr;
bool prevWasUnderscore = false;
for (int entryIdx = 0; entryIdx < entryLen; entryIdx++)
{
char entryC = entry[entryIdx];
if (entryC == '_')
{
prevWasUnderscore = true;
continue;
}
if ((entryIdx == 0) || (prevWasUnderscore) || (isupper((uint8)entryC) || (isdigit((uint8)entryC))))
{
if (strnicmp(filter, entry + entryIdx, filterLen) == 0)
return true;
if (checkInitials)
*(initialStrP++) = entryC;
}
prevWasUnderscore = false;
if (filterLen == 1)
break; // Don't check inners for single-character case
}
if (!checkInitials)
return false;
*(initialStrP++) = 0;
return strnicmp(filter, initialStr, filterLen) == 0;
*/
} }
void AutoCompleteBase::Clear() void AutoCompleteBase::Clear()

View file

@ -17,12 +17,15 @@ public:
const char* mDocumentation; const char* mDocumentation;
int8 mNamePrefixCount; int8 mNamePrefixCount;
int mScore; int mScore;
uint8 mMatches[256]; uint8* mMatches;
uint8 mMatchesLength;
public: public:
AutoCompleteEntry() AutoCompleteEntry()
{ {
mNamePrefixCount = 0; mNamePrefixCount = 0;
mMatches = nullptr;
mMatchesLength = 0;
} }
AutoCompleteEntry(const char* entryType, const char* display) AutoCompleteEntry(const char* entryType, const char* display)
@ -32,6 +35,8 @@ public:
mDocumentation = NULL; mDocumentation = NULL;
mNamePrefixCount = 0; mNamePrefixCount = 0;
mScore = 0; mScore = 0;
mMatches = nullptr;
mMatchesLength = 0;
} }
AutoCompleteEntry(const char* entryType, const StringImpl& display) AutoCompleteEntry(const char* entryType, const StringImpl& display)
@ -41,6 +46,8 @@ public:
mDocumentation = NULL; mDocumentation = NULL;
mNamePrefixCount = 0; mNamePrefixCount = 0;
mScore = 0; mScore = 0;
mMatches = nullptr;
mMatchesLength = 0;
} }
AutoCompleteEntry(const char* entryType, const StringImpl& display, int namePrefixCount) AutoCompleteEntry(const char* entryType, const StringImpl& display, int namePrefixCount)
@ -50,8 +57,10 @@ public:
mDocumentation = NULL; mDocumentation = NULL;
mNamePrefixCount = (int8)namePrefixCount; mNamePrefixCount = (int8)namePrefixCount;
mScore = 0; mScore = 0;
mMatches = nullptr;
mMatchesLength = 0;
} }
bool operator==(const AutoCompleteEntry& other) const bool operator==(const AutoCompleteEntry& other) const
{ {
return strcmp(mDisplay, other.mDisplay) == 0; return strcmp(mDisplay, other.mDisplay) == 0;

View file

@ -8024,26 +8024,25 @@ void BfCompiler::GenerateAutocompleteInfo()
autoCompleteResultString += '@'; autoCompleteResultString += '@';
autoCompleteResultString += String(entry->mDisplay); autoCompleteResultString += String(entry->mDisplay);
autoCompleteResultString += "\x02"; if (entry->mMatchesLength > 0)
for (int i = 0; i < 256; i++)
{ {
int match = entry->mMatches[i]; autoCompleteResultString += "\x02";
for (int i = 0; i < entry->mMatchesLength; i++)
{
int match = entry->mMatches[i];
// no more matches after this // Need max 3 chars (largest Hex (FF) + '\0')
if (match == 0 && i != 0) char buffer[3];
break;
// Need max 3 chars (largest Hex (FF) + '\0') _itoa_s(match, buffer, 16);
char buffer[3];
_itoa_s(match, buffer, 16); autoCompleteResultString += String(buffer);
autoCompleteResultString += ",";
}
autoCompleteResultString += String(buffer); autoCompleteResultString += "X";
autoCompleteResultString += ",";
} }
autoCompleteResultString += "X";
if (entry->mDocumentation != NULL) if (entry->mDocumentation != NULL)
{ {
autoCompleteResultString += '\x03'; autoCompleteResultString += '\x03';