Skip to content
This repository was archived by the owner on Apr 14, 2022. It is now read-only.

Add loose fuzzy matching for workspace symbol queries #1950

Merged
merged 2 commits into from
Mar 18, 2020
Merged
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
16 changes: 15 additions & 1 deletion src/LanguageServer/Impl/Indexing/SymbolIndex.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ private IEnumerable<FlatSymbol> WorkspaceSymbolsQuery(string path, string query,
var sym = symAndPar.symbol;
return DecorateWithParentsName((sym.Children ?? Enumerable.Empty<HierarchicalSymbol>()).ToList(), sym.Name);
});
return treeSymbols.Where(sym => sym.symbol.Name.ContainsOrdinal(query, ignoreCase: true))
return treeSymbols.Where(sym => FuzzyMatch(query, sym.symbol.Name))
.Select(sym => new FlatSymbol(sym.symbol.Name, sym.symbol.Kind, path, sym.symbol.SelectionRange, sym.parentName, sym.symbol._existInAllVariable));
}

Expand All @@ -114,5 +114,19 @@ private IMostRecentDocumentSymbols MakeMostRecentDocSymbols(string path) {
public void Dispose() {
_disposables.TryDispose();
}

private static bool FuzzyMatch(string pattern, string name) {
var patternPos = 0;
var namePos = 0;

while (patternPos < pattern.Length && namePos < name.Length) {
if (char.ToLowerInvariant(pattern[patternPos]) == char.ToLowerInvariant(name[namePos])) {
patternPos++;
}
namePos++;
}

return patternPos == pattern.Length;
}
}
}
17 changes: 17 additions & 0 deletions src/LanguageServer/Test/SymbolIndexTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,23 @@ public async Task IndexWorkspaceSymbolsCaseInsensitiveAsync() {
}
}

[TestMethod, Priority(0)]
public async Task IndexWorkspaceSymbolsFuzzyAsync() {
const string code = @"class FXoo(object):
def foxo(self, x): ...";

using (var index = MakeSymbolIndex()) {
var path = TestData.GetDefaultModulePath();
index.Add(path, DocumentWithAst(code));

var symbols = await index.WorkspaceSymbolsAsync("foo", maxSymbols);
symbols.Should().BeEquivalentToWithStrictOrdering(new[] {
new FlatSymbol("FXoo", SymbolKind.Class, path, new SourceSpan(1, 7, 1, 11)),
new FlatSymbol("foxo", SymbolKind.Method, path, new SourceSpan(2, 9, 2, 13), "FXoo"),
});
}
}

[TestMethod, Priority(0)]
public void MarkAsPendingWaitsForUpdates() {
using (var index = MakeSymbolIndex()) {
Expand Down