diff --git a/src/LanguageServer/Impl/Indexing/SymbolIndex.cs b/src/LanguageServer/Impl/Indexing/SymbolIndex.cs index 7b97905b7..5d901f634 100644 --- a/src/LanguageServer/Impl/Indexing/SymbolIndex.cs +++ b/src/LanguageServer/Impl/Indexing/SymbolIndex.cs @@ -98,7 +98,7 @@ private IEnumerable WorkspaceSymbolsQuery(string path, string query, var sym = symAndPar.symbol; return DecorateWithParentsName((sym.Children ?? Enumerable.Empty()).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)); } @@ -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; + } } } diff --git a/src/LanguageServer/Test/SymbolIndexTests.cs b/src/LanguageServer/Test/SymbolIndexTests.cs index 861eb1cec..d695ad8ce 100644 --- a/src/LanguageServer/Test/SymbolIndexTests.cs +++ b/src/LanguageServer/Test/SymbolIndexTests.cs @@ -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()) {