From 04888b8f1012aa01d00cf1bc0072cf0dc71d7243 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Sat, 18 Dec 2021 22:28:44 +0100 Subject: [PATCH] Improved performance of fuzzy string matching --- IDE/src/ui/AutoComplete.bf | 2 +- IDEHelper/Compiler/BfAutoComplete.cpp | 109 +++++++++++++------------- IDEHelper/Compiler/BfAutoComplete.h | 13 ++- IDEHelper/Compiler/BfCompiler.cpp | 25 +++--- 4 files changed, 79 insertions(+), 70 deletions(-) diff --git a/IDE/src/ui/AutoComplete.bf b/IDE/src/ui/AutoComplete.bf index 77c242fa..aace0a59 100644 --- a/IDE/src/ui/AutoComplete.bf +++ b/IDE/src/ui/AutoComplete.bf @@ -409,7 +409,7 @@ namespace IDE.ui for(char32 c in mEntryDisplay.DecodedChars) loop: { - if(mMatchIndices.Contains((uint8)index)) + if(mMatchIndices?.Contains((uint8)index) == true) { g.PushColor(DarkTheme.COLOR_MENU_FOCUSED); defer:loop g.PopColor(); diff --git a/IDEHelper/Compiler/BfAutoComplete.cpp b/IDEHelper/Compiler/BfAutoComplete.cpp index f15efa6e..288e1b8f 100644 --- a/IDEHelper/Compiler/BfAutoComplete.cpp +++ b/IDEHelper/Compiler/BfAutoComplete.cpp @@ -30,20 +30,68 @@ AutoCompleteBase::~AutoCompleteBase() 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; + + 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); } 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; + + 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); } AutoCompleteEntry* AutoCompleteBase::AddEntry(const AutoCompleteEntry& entry) -{ +{ if (mEntriesSet.mAllocSize == 0) { mEntriesSet.Reserve(128); @@ -58,13 +106,16 @@ AutoCompleteEntry* AutoCompleteBase::AddEntry(const AutoCompleteEntry& entry) int size = (int)strlen(display) + 1; insertedEntry->mDisplay = (char*)mAlloc.AllocBytes(size); memcpy((char*)insertedEntry->mDisplay, display, size); + + insertedEntry->mMatches = (uint8*)mAlloc.AllocBytes(insertedEntry->mMatchesLength); + memcpy((char*)insertedEntry->mMatches, entry.mMatches, insertedEntry->mMatchesLength); } return insertedEntry; } bool AutoCompleteBase::DoesFilterMatch(const char* entry, const char* filter, int& score, uint8* matches, int maxMatches) -{ +{ if (mIsGetDefinition) { int entryLen = (int)strlen(entry); @@ -95,57 +146,7 @@ bool AutoCompleteBase::DoesFilterMatch(const char* entry, const char* filter, in return false; } - // TODO: also do matches (but probably optimize them) 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() diff --git a/IDEHelper/Compiler/BfAutoComplete.h b/IDEHelper/Compiler/BfAutoComplete.h index 881a2c90..93b973a6 100644 --- a/IDEHelper/Compiler/BfAutoComplete.h +++ b/IDEHelper/Compiler/BfAutoComplete.h @@ -17,12 +17,15 @@ public: const char* mDocumentation; int8 mNamePrefixCount; int mScore; - uint8 mMatches[256]; + uint8* mMatches; + uint8 mMatchesLength; public: AutoCompleteEntry() { mNamePrefixCount = 0; + mMatches = nullptr; + mMatchesLength = 0; } AutoCompleteEntry(const char* entryType, const char* display) @@ -32,6 +35,8 @@ public: mDocumentation = NULL; mNamePrefixCount = 0; mScore = 0; + mMatches = nullptr; + mMatchesLength = 0; } AutoCompleteEntry(const char* entryType, const StringImpl& display) @@ -41,6 +46,8 @@ public: mDocumentation = NULL; mNamePrefixCount = 0; mScore = 0; + mMatches = nullptr; + mMatchesLength = 0; } AutoCompleteEntry(const char* entryType, const StringImpl& display, int namePrefixCount) @@ -50,8 +57,10 @@ public: mDocumentation = NULL; mNamePrefixCount = (int8)namePrefixCount; mScore = 0; + mMatches = nullptr; + mMatchesLength = 0; } - + bool operator==(const AutoCompleteEntry& other) const { return strcmp(mDisplay, other.mDisplay) == 0; diff --git a/IDEHelper/Compiler/BfCompiler.cpp b/IDEHelper/Compiler/BfCompiler.cpp index 8ce3868c..cc540e80 100644 --- a/IDEHelper/Compiler/BfCompiler.cpp +++ b/IDEHelper/Compiler/BfCompiler.cpp @@ -8024,26 +8024,25 @@ void BfCompiler::GenerateAutocompleteInfo() autoCompleteResultString += '@'; autoCompleteResultString += String(entry->mDisplay); - autoCompleteResultString += "\x02"; - for (int i = 0; i < 256; i++) + if (entry->mMatchesLength > 0) { - 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 - if (match == 0 && i != 0) - break; + // Need max 3 chars (largest Hex (FF) + '\0') + char buffer[3]; - // Need max 3 chars (largest Hex (FF) + '\0') - char buffer[3]; + _itoa_s(match, buffer, 16); - _itoa_s(match, buffer, 16); + autoCompleteResultString += String(buffer); + autoCompleteResultString += ","; + } - autoCompleteResultString += String(buffer); - autoCompleteResultString += ","; + autoCompleteResultString += "X"; } - autoCompleteResultString += "X"; - if (entry->mDocumentation != NULL) { autoCompleteResultString += '\x03';