From c2c7431620b4dd3c5949bb7275c76e25c91b4a96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Wed, 15 Dec 2021 11:01:14 +0100 Subject: [PATCH] Fixed autocomplete flickering while typing --- IDE/src/ui/AutoComplete.bf | 118 ++++++++---------- IDEHelper/BeefProj.toml | 8 ++ IDEHelper/Compiler/BfAutoComplete.cpp | 2 +- IDEHelper/Compiler/BfCompiler.cpp | 4 - IDEHelper/IDEHelper.vcxproj | 2 +- IDEHelper/IDEHelper.vcxproj.filters | 7 +- .../{Compiler => third_party}/FtsFuzzyMatch.h | 6 + 7 files changed, 75 insertions(+), 72 deletions(-) rename IDEHelper/{Compiler => third_party}/FtsFuzzyMatch.h (96%) diff --git a/IDE/src/ui/AutoComplete.bf b/IDE/src/ui/AutoComplete.bf index c1a74748..77c242fa 100644 --- a/IDE/src/ui/AutoComplete.bf +++ b/IDE/src/ui/AutoComplete.bf @@ -1597,70 +1597,22 @@ namespace IDE.ui mInvokeWidget.mIgnoreMove += ignoreMove ? 1 : -1; } + // IDEHelper/third_party/FtsFuzzyMatch.h + [CallingConvention(.Stdcall), CLink] + static extern bool fts_fuzzy_match(char8* pattern, char8* str, ref int outScore, uint8* matches, int maxMatches); + bool DoesFilterMatch(String entry, String filter) - { + { if (filter.Length == 0) return true; - char8* entryPtr = entry.Ptr; - char8* filterPtr = filter.Ptr; - - int filterLen = (int)filter.Length; - int entryLen = (int)entry.Length; - - bool hasUnderscore = false; - bool checkInitials = filterLen > 1; - for (int i = 0; i < (int)filterLen; i++) - { - char8 c = filterPtr[i]; - if (c == '_') - hasUnderscore = true; - else if (filterPtr[i].IsLower) - checkInitials = false; - } - - if (hasUnderscore) - //return strnicmp(filter, entry, filterLen) == 0; - return (entryLen >= filterLen) && (String.Compare(entryPtr, filterLen, filterPtr, filterLen, true) == 0); - - char8[256] initialStr; - char8* initialStrP = &initialStr; - - //String initialStr; - bool prevWasUnderscore = false; - - for (int entryIdx = 0; entryIdx < entryLen; entryIdx++) - { - char8 entryC = entryPtr[entryIdx]; - - if (entryC == '_') - { - prevWasUnderscore = true; - continue; - } - - if ((entryIdx == 0) || (prevWasUnderscore) || (entryC.IsUpper) || (entryC.IsDigit)) - { - /*if (strnicmp(filter, entry + entryIdx, filterLen) == 0) - return true;*/ - if ((entryLen - entryIdx >= filterLen) && (String.Compare(entryPtr + entryIdx, filterLen, filterPtr, filterLen, true) == 0)) - return true; - if (checkInitials) - *(initialStrP++) = entryC; - } - prevWasUnderscore = false; - - if (filterLen == 1) - break; // Don't check inners for single-character case - } - - if (!checkInitials) + if (filter.Length > entry.Length) return false; - int initialLen = initialStrP - (char8*)&initialStr; - return (initialLen >= filterLen) && (String.Compare(&initialStr, filterLen, filterPtr, filterLen, true) == 0); - //*(initialStrP++) = 0; - //return strnicmp(filter, initialStr, filterLen) == 0; + int score = 0; + uint8[256] matches = ?; + + return fts_fuzzy_match(filter.CStr(), entry.CStr(), ref score, &matches, matches.Count); } void UpdateData(String selectString, bool changedAfterInfo) @@ -1718,7 +1670,7 @@ namespace IDE.ui for (int i < mAutoCompleteListWidget.mFullEntryList.Count) { var entry = mAutoCompleteListWidget.mFullEntryList[i]; - //if (String.Compare(entry.mEntryDisplay, 0, curString, 0, curString.Length, true) == 0) + if (DoesFilterMatch(entry.mEntryDisplay, curString)) { mAutoCompleteListWidget.mEntryList.Add(entry); @@ -1876,6 +1828,7 @@ namespace IDE.ui public void UpdateInfo(String info) { + List matchIndices = new:ScopedAlloc! .(256); for (var entryView in info.Split('\n')) { StringView entryType = StringView(entryView); @@ -1886,13 +1839,35 @@ namespace IDE.ui entryDisplay = StringView(entryView, tabPos + 1); entryType = StringView(entryType, 0, tabPos); } + + StringView matches = default; + int matchesPos = entryDisplay.IndexOf('\x02'); + matchIndices.Clear(); + if (matchesPos != -1) + { + matches = StringView(entryDisplay, matchesPos + 1); + entryDisplay = StringView(entryDisplay, 0, matchesPos); + + for(var sub in matches.Split(',')) + { + if(sub.StartsWith('X')) + break; + + var result = int64.Parse(sub, .HexNumber); + + Debug.Assert((result case .Ok(let value)) && value <= uint8.MaxValue); + + // TODO(FUZZY): we could save start and length instead of single chars + matchIndices.Add((uint8)result.Value); + } + } StringView documentation = default; - int docPos = entryDisplay.IndexOf('\x03'); + int docPos = matches.IndexOf('\x03'); if (docPos != -1) { - documentation = StringView(entryDisplay, docPos + 1); - entryDisplay = StringView(entryDisplay, 0, docPos); + documentation = StringView(matches, docPos + 1); + matches = StringView(matches, 0, docPos); } StringView entryInsert = default; @@ -1915,15 +1890,27 @@ namespace IDE.ui case "select": default: { - if ((!documentation.IsEmpty) && (mAutoCompleteListWidget != null)) + if (((!documentation.IsEmpty) || (!matchIndices.IsEmpty)) && (mAutoCompleteListWidget != null)) { while (entryIdx < mAutoCompleteListWidget.mEntryList.Count) { let entry = mAutoCompleteListWidget.mEntryList[entryIdx]; if ((entry.mEntryDisplay == entryDisplay) && (entry.mEntryType == entryType)) { - if (entry.mDocumentation == null) + if (!matchIndices.IsEmpty) + { + if (entry.mMatchIndices == null) + entry.mMatchIndices = new:(mAutoCompleteListWidget.[Friend]mAlloc) List(matchIndices.GetEnumerator()); + else + { + entry.mMatchIndices.Clear(); + entry.mMatchIndices.AddRange(matchIndices); + } + } + + if ((!documentation.IsEmpty) && entry.mDocumentation == null) entry.mDocumentation = new:(mAutoCompleteListWidget.[Friend]mAlloc) String(documentation); + break; } entryIdx++; @@ -2017,6 +2004,7 @@ namespace IDE.ui entryDisplay = StringView(entryView, tabPos + 1); entryType = StringView(entryType, 0, tabPos); } + StringView matches = default; int matchesPos = entryDisplay.IndexOf('\x02'); matchIndices.Clear(); @@ -2027,7 +2015,7 @@ namespace IDE.ui for(var sub in matches.Split(',')) { - if(sub == "X") + if(sub.StartsWith('X')) break; var result = int64.Parse(sub, .HexNumber); diff --git a/IDEHelper/BeefProj.toml b/IDEHelper/BeefProj.toml index 7f6d1208..f773ae51 100644 --- a/IDEHelper/BeefProj.toml +++ b/IDEHelper/BeefProj.toml @@ -508,3 +508,11 @@ Path = "X86Target.h" [[ProjectFolder.Items]] Type = "Source" Path = "X86XmmInfo.cpp" + +[[ProjectFolder.Items]] +Type = "Folder" +Name = "third_party" + +[[ProjectFolder.Items.Items]] +Type = "Source" +Path = "third_party/FtsFuzzyMatch.h" diff --git a/IDEHelper/Compiler/BfAutoComplete.cpp b/IDEHelper/Compiler/BfAutoComplete.cpp index 22b87c9d..b83b2d46 100644 --- a/IDEHelper/Compiler/BfAutoComplete.cpp +++ b/IDEHelper/Compiler/BfAutoComplete.cpp @@ -7,7 +7,7 @@ #include "BfResolvedTypeUtils.h" #define FTS_FUZZY_MATCH_IMPLEMENTATION -#include "FtsFuzzyMatch.h" +#include "../third_party/FtsFuzzyMatch.h" #pragma warning(disable:4996) diff --git a/IDEHelper/Compiler/BfCompiler.cpp b/IDEHelper/Compiler/BfCompiler.cpp index bf329458..f45e4b49 100644 --- a/IDEHelper/Compiler/BfCompiler.cpp +++ b/IDEHelper/Compiler/BfCompiler.cpp @@ -8007,9 +8007,7 @@ void BfCompiler::GenerateAutocompleteInfo() } std::sort(entries.begin(), entries.end(), [](AutoCompleteEntry* lhs, AutoCompleteEntry* rhs) { - // TODO(FUZZY): SORT BY Score return lhs->mScore > rhs->mScore; - //return stricmp(lhs->mDisplay, rhs->mDisplay) < 0; }); String docString; @@ -8024,8 +8022,6 @@ void BfCompiler::GenerateAutocompleteInfo() autoCompleteResultString += '@'; autoCompleteResultString += String(entry->mDisplay); - // TODO(FUZZY): OUTPUT - // TODO(FUZZY): this is not really efficient autoCompleteResultString += "\x02"; for (int i = 0; i < 256; i++) { diff --git a/IDEHelper/IDEHelper.vcxproj b/IDEHelper/IDEHelper.vcxproj index 59635b05..f2e39a4f 100644 --- a/IDEHelper/IDEHelper.vcxproj +++ b/IDEHelper/IDEHelper.vcxproj @@ -400,7 +400,7 @@ - + diff --git a/IDEHelper/IDEHelper.vcxproj.filters b/IDEHelper/IDEHelper.vcxproj.filters index ea9206d1..5f026346 100644 --- a/IDEHelper/IDEHelper.vcxproj.filters +++ b/IDEHelper/IDEHelper.vcxproj.filters @@ -24,6 +24,9 @@ {83b97406-2f83-49ad-bbbc-3ff70ecda6bb} + + {d36777f2-b326-4a8c-84a3-5c2f39153f75} + @@ -399,6 +402,8 @@ Compiler - + + third_party + \ No newline at end of file diff --git a/IDEHelper/Compiler/FtsFuzzyMatch.h b/IDEHelper/third_party/FtsFuzzyMatch.h similarity index 96% rename from IDEHelper/Compiler/FtsFuzzyMatch.h rename to IDEHelper/third_party/FtsFuzzyMatch.h index 77a58b97..988adf82 100644 --- a/IDEHelper/Compiler/FtsFuzzyMatch.h +++ b/IDEHelper/third_party/FtsFuzzyMatch.h @@ -47,6 +47,7 @@ namespace fts { static bool fuzzy_match(char const* pattern, char const* str, int& outScore, uint8_t* matches, int maxMatches); } +BF_EXPORT bool BF_CALLTYPE fts_fuzzy_match(char const* pattern, char const* str, int& outScore, uint8_t* matches, int maxMatches); #ifdef FTS_FUZZY_MATCH_IMPLEMENTATION namespace fts { @@ -245,6 +246,11 @@ namespace fts { } } // namespace fts +BF_EXPORT bool BF_CALLTYPE fts_fuzzy_match(char const* pattern, char const* str, int& outScore, uint8_t* matches, int maxMatches) +{ + return fts::fuzzy_match(pattern, str, outScore, matches, maxMatches); +} + #endif // FTS_FUZZY_MATCH_IMPLEMENTATION #endif // FTS_FUZZY_MATCH_H