Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fuzzy matching instead of startsWith for filtering #734

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion dsymbol/dub.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"targetType": "library",
"dependencies": {
"libdparse": ">=0.20.0 <1.0.0",
"emsi_containers": "~>0.9.0"
"emsi_containers": "~>0.9.0",
"fuzzymatch": "~>1.0"
}
}
4 changes: 2 additions & 2 deletions dsymbol/src/dsymbol/ufcs.d
Original file line number Diff line number Diff line change
Expand Up @@ -231,12 +231,12 @@ private DSymbol*[] getUFCSSymbolsForDotCompletion(const(DSymbol)* symbolType, Sc
// local appender
FilteredAppender!((DSymbol* a) =>
a.isCallableWithArg(symbolType)
&& toUpper(a.name.data).startsWith(toUpper(partial)),
&& prettyFuzzyMatch(a.name.data, partial),
DSymbol*[]) localAppender;
// global appender
FilteredAppender!((DSymbol* a) =>
a.isCallableWithArg(symbolType, true)
&& toUpper(a.name.data).startsWith(toUpper(partial)),
&& prettyFuzzyMatch(a.name.data, partial),
DSymbol*[]) globalAppender;

getUFCSSymbols(localAppender, globalAppender, completionScope, cursorPosition);
Expand Down
16 changes: 16 additions & 0 deletions dsymbol/src/dsymbol/utils.d
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,19 @@ unittest
i = skipParenReverseBefore(t, i, tok!")", tok!"(");
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);
}
1 change: 1 addition & 0 deletions dub.selections.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"versions": {
"dsymbol": "0.14.1",
"emsi_containers": "0.9.0",
"fuzzymatch": "1.0.0",
"libdparse": "0.22.0",
"msgpack-d": "1.0.4",
"stdx-allocator": "2.77.5"
Expand Down
8 changes: 3 additions & 5 deletions src/dcd/server/autocomplete/complete.d
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,6 @@ AutocompleteResponse dotCompletion(T)(T beforeTokens, const(Token)[] tokenArray,
{
partial = t.text[0 .. cursorPosition - t.index];
// 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;
try validate(partial);
catch (UTFException)
Expand Down Expand Up @@ -513,7 +512,7 @@ void setCompletions(T)(ref AutocompleteResponse response,
foreach (sym; s.opSlice())
{
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)
&& sym.name[0] != '*'
&& mightBeRelevantInCompletionScope(sym, completionScope))
Expand All @@ -531,7 +530,7 @@ void setCompletions(T)(ref AutocompleteResponse response,
{
auto currentSymbols = completionScope.getSymbolsInCursorScope(cursorPosition);
foreach (s; currentSymbols.filter!(a => isPublicCompletionKind(a.kind)
&& toUpper(a.name.data).startsWith(toUpper(partial))
&& prettyFuzzyMatch(a.name.data, partial)
&& mightBeRelevantInCompletionScope(a, completionScope)))
{
response.completions ~= makeSymbolCompletionInfo(s, s.kind);
Expand All @@ -549,8 +548,7 @@ void setCompletions(T)(ref AutocompleteResponse response,
&& a.kind != CompletionKind.importSymbol
&& a.kind != CompletionKind.dummy
&& a.symbolFile == "stdin"
&& (partial !is null && toUpper(a.name.data).startsWith(toUpper(partial))
|| partial is null)
&& prettyFuzzyMatch(a.name.data, partial)
&& mightBeRelevantInCompletionScope(a, completionScope)))
{
response.completions ~= makeSymbolCompletionInfo(s, s.kind);
Expand Down
3 changes: 3 additions & 0 deletions tests/tc021/expected.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ idouble k
ifloat k
int k
ireal k
string l
uint k
void k
3 changes: 3 additions & 0 deletions tests/tc029/expected1.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ cfloat k
char k
complicatedLess l
creal k
dchar k
ucent k
wchar k
2 changes: 2 additions & 0 deletions tests/tc051/expected.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
identifiers
Foo s
cfloat k
float k
ifloat k
5 changes: 5 additions & 0 deletions tests/tc_accesschain_type/expected.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
identifiers
alignof k
foo f
mangleof k
sizeof k
stringof k
tupleof k
1 change: 1 addition & 0 deletions tests/tc_complete_kw/expected3.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
identifiers
init k
internal v
3 changes: 3 additions & 0 deletions tests/tc_extended_ditto/expected1.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
identifiers
cfloat k cfloat
float k float
foo f void foo() stdin 26 my documentation void
foo f void foo(int i) stdin 49 my documentation void
ifloat k ifloat