Fuzzy matching instead of startsWith for filtering

Experimental, subject to change
This commit is contained in:
WebFreak001 2023-03-20 04:03:17 +01:00
parent c324b60da3
commit d6376c4881
No known key found for this signature in database
GPG Key ID: AEFC88D11109D1AA
11 changed files with 41 additions and 8 deletions

View File

@ -7,6 +7,7 @@
"targetType": "library", "targetType": "library",
"dependencies": { "dependencies": {
"libdparse": ">=0.20.0 <1.0.0", "libdparse": ">=0.20.0 <1.0.0",
"emsi_containers": "~>0.9.0" "emsi_containers": "~>0.9.0",
"fuzzymatch": "~>1.0"
} }
} }

View File

@ -231,12 +231,12 @@ private DSymbol*[] getUFCSSymbolsForDotCompletion(const(DSymbol)* symbolType, Sc
// local appender // local appender
FilteredAppender!((DSymbol* a) => FilteredAppender!((DSymbol* a) =>
a.isCallableWithArg(symbolType) a.isCallableWithArg(symbolType)
&& toUpper(a.name.data).startsWith(toUpper(partial)), && prettyFuzzyMatch(a.name.data, partial),
DSymbol*[]) localAppender; DSymbol*[]) localAppender;
// global appender // global appender
FilteredAppender!((DSymbol* a) => FilteredAppender!((DSymbol* a) =>
a.isCallableWithArg(symbolType, true) a.isCallableWithArg(symbolType, true)
&& toUpper(a.name.data).startsWith(toUpper(partial)), && prettyFuzzyMatch(a.name.data, partial),
DSymbol*[]) globalAppender; DSymbol*[]) globalAppender;
getUFCSSymbols(localAppender, globalAppender, completionScope, cursorPosition); getUFCSSymbols(localAppender, globalAppender, completionScope, cursorPosition);

View File

@ -153,3 +153,19 @@ unittest
i = skipParenReverseBefore(t, i, tok!")", tok!"("); i = skipParenReverseBefore(t, i, tok!")", tok!"(");
assert(i == 1); assert(i == 1);
} }
/// Checks if `doesThis` roughly starts with `matchThis`, case-insensitive
bool prettyFuzzyMatch(scope const(char)[] doesThis, scope const(char)[] matchThis) @safe pure nothrow @nogc
{
import fuzzymatch;
if (!matchThis.length)
return true;
// return false if identifier starts with _, but search doesn't or vice-versa
if (doesThis.length && (doesThis[0] == '_' && matchThis[0] != '_'
|| doesThis[0] != '_' && matchThis[0] == '_'))
return false;
return fuzzyMatchesString(doesThis, matchThis);
}

View File

@ -3,6 +3,7 @@
"versions": { "versions": {
"dsymbol": "0.14.1", "dsymbol": "0.14.1",
"emsi_containers": "0.9.0", "emsi_containers": "0.9.0",
"fuzzymatch": "1.0.0",
"libdparse": "0.22.0", "libdparse": "0.22.0",
"msgpack-d": "1.0.4", "msgpack-d": "1.0.4",
"stdx-allocator": "2.77.5" "stdx-allocator": "2.77.5"

View File

@ -188,7 +188,6 @@ AutocompleteResponse dotCompletion(T)(T beforeTokens, const(Token)[] tokenArray,
{ {
partial = t.text[0 .. cursorPosition - t.index]; partial = t.text[0 .. cursorPosition - t.index];
// issue 442 - prevent `partial` to start in the middle of a MBC // issue 442 - prevent `partial` to start in the middle of a MBC
// since later there's a non-nothrow call to `toUpper`
import std.utf : validate, UTFException; import std.utf : validate, UTFException;
try validate(partial); try validate(partial);
catch (UTFException) catch (UTFException)
@ -513,7 +512,7 @@ void setCompletions(T)(ref AutocompleteResponse response,
foreach (sym; s.opSlice()) foreach (sym; s.opSlice())
{ {
if (sym.name !is null && sym.name.length > 0 && isPublicCompletionKind(sym.kind) if (sym.name !is null && sym.name.length > 0 && isPublicCompletionKind(sym.kind)
&& (p is null ? true : toUpper(sym.name.data).startsWith(toUpper(p))) && prettyFuzzyMatch(sym.name.data, p)
&& !r.completions.canFind!(a => a.identifier == sym.name) && !r.completions.canFind!(a => a.identifier == sym.name)
&& sym.name[0] != '*' && sym.name[0] != '*'
&& mightBeRelevantInCompletionScope(sym, completionScope)) && mightBeRelevantInCompletionScope(sym, completionScope))
@ -531,7 +530,7 @@ void setCompletions(T)(ref AutocompleteResponse response,
{ {
auto currentSymbols = completionScope.getSymbolsInCursorScope(cursorPosition); auto currentSymbols = completionScope.getSymbolsInCursorScope(cursorPosition);
foreach (s; currentSymbols.filter!(a => isPublicCompletionKind(a.kind) foreach (s; currentSymbols.filter!(a => isPublicCompletionKind(a.kind)
&& toUpper(a.name.data).startsWith(toUpper(partial)) && prettyFuzzyMatch(a.name.data, partial)
&& mightBeRelevantInCompletionScope(a, completionScope))) && mightBeRelevantInCompletionScope(a, completionScope)))
{ {
response.completions ~= makeSymbolCompletionInfo(s, s.kind); response.completions ~= makeSymbolCompletionInfo(s, s.kind);
@ -549,8 +548,7 @@ void setCompletions(T)(ref AutocompleteResponse response,
&& a.kind != CompletionKind.importSymbol && a.kind != CompletionKind.importSymbol
&& a.kind != CompletionKind.dummy && a.kind != CompletionKind.dummy
&& a.symbolFile == "stdin" && a.symbolFile == "stdin"
&& (partial !is null && toUpper(a.name.data).startsWith(toUpper(partial)) && prettyFuzzyMatch(a.name.data, partial)
|| partial is null)
&& mightBeRelevantInCompletionScope(a, completionScope))) && mightBeRelevantInCompletionScope(a, completionScope)))
{ {
response.completions ~= makeSymbolCompletionInfo(s, s.kind); response.completions ~= makeSymbolCompletionInfo(s, s.kind);

View File

@ -3,3 +3,6 @@ idouble k
ifloat k ifloat k
int k int k
ireal k ireal k
string l
uint k
void k

View File

@ -6,3 +6,6 @@ cfloat k
char k char k
complicatedLess l complicatedLess l
creal k creal k
dchar k
ucent k
wchar k

View File

@ -1,3 +1,5 @@
identifiers identifiers
Foo s Foo s
cfloat k
float k float k
ifloat k

View File

@ -1,2 +1,7 @@
identifiers identifiers
alignof k
foo f foo f
mangleof k
sizeof k
stringof k
tupleof k

View File

@ -1,2 +1,3 @@
identifiers identifiers
init k
internal v internal v

View File

@ -1,3 +1,6 @@
identifiers identifiers
cfloat k cfloat
float k float
foo f void foo() stdin 26 my documentation void foo f void foo() stdin 26 my documentation void
foo f void foo(int i) stdin 49 my documentation void foo f void foo(int i) stdin 49 my documentation void
ifloat k ifloat