From 54d9230b66ba039e51371a598a9f9abafadc8c33 Mon Sep 17 00:00:00 2001 From: Dmitry Goncharenko <30739723+dmgonch@users.noreply.github.com> Date: Wed, 19 Sep 2018 23:14:05 -0700 Subject: [PATCH] Introduce configuration for "Go to Symbol in Workspace" (#1284) * Added to IOmniSharpEnvironment * Revert "Added to IOmniSharpEnvironment" This reverts commit e4a962aca2e351429f11ef945d7d34ec44f22179. * Plugged in Find Symbols Options * Ignore FindSymbolsOptions.MaxItemsToReturn <=0 * resolved merge conflict * Fix build break * Per CR feedback moved new configuration parameters to FindSymbolsRequest * Switch MinFilterLength and MaxItemsToReturn in FindSymbolsRequest to be int? * Use named args for nulls and consts in test as per PR feedback --- .../v1/FindSymbols/FindSymbolsRequest.cs | 2 + .../Navigation/FindSymbolsHandler.cs | 8 +++- .../Services/Navigation/FindSymbolsService.cs | 10 ++++- .../Extensions/SolutionExtensions.cs | 18 +++++++- .../OmniSharp.Cake.Tests/FindSymbolsFacts.cs | 27 +++++++++--- .../FindSymbolsFacts.cs | 43 +++++++++++++++++-- 6 files changed, 95 insertions(+), 13 deletions(-) diff --git a/src/OmniSharp.Abstractions/Models/v1/FindSymbols/FindSymbolsRequest.cs b/src/OmniSharp.Abstractions/Models/v1/FindSymbols/FindSymbolsRequest.cs index 6be5dc040a..b8976c1d42 100644 --- a/src/OmniSharp.Abstractions/Models/v1/FindSymbols/FindSymbolsRequest.cs +++ b/src/OmniSharp.Abstractions/Models/v1/FindSymbols/FindSymbolsRequest.cs @@ -7,5 +7,7 @@ public class FindSymbolsRequest : IRequest { public string Language { get; set; } public string Filter { get; set; } + public int? MinFilterLength { get; set; } + public int? MaxItemsToReturn { get; set; } } } diff --git a/src/OmniSharp.Cake/Services/RequestHandlers/Navigation/FindSymbolsHandler.cs b/src/OmniSharp.Cake/Services/RequestHandlers/Navigation/FindSymbolsHandler.cs index 03594c8dd2..b53b68a258 100644 --- a/src/OmniSharp.Cake/Services/RequestHandlers/Navigation/FindSymbolsHandler.cs +++ b/src/OmniSharp.Cake/Services/RequestHandlers/Navigation/FindSymbolsHandler.cs @@ -21,12 +21,18 @@ public FindSymbolsHandler(OmniSharpWorkspace workspace) public override Task Handle(FindSymbolsRequest request) { + if (request?.Filter?.Length < request?.MinFilterLength.GetValueOrDefault()) + { + return Task.FromResult(new QuickFixResponse { QuickFixes = Array.Empty() }); + } + Func isMatch = candidate => request != null ? candidate.IsValidCompletionFor(request.Filter) : true; - return Workspace.CurrentSolution.FindSymbols(isMatch, LanguageNames.Cake); + int maxItemsToReturn = (request?.MaxItemsToReturn).GetValueOrDefault(); + return Workspace.CurrentSolution.FindSymbols(isMatch, LanguageNames.Cake, maxItemsToReturn); } protected override Task TranslateResponse(QuickFixResponse response, FindSymbolsRequest request) diff --git a/src/OmniSharp.Roslyn.CSharp/Services/Navigation/FindSymbolsService.cs b/src/OmniSharp.Roslyn.CSharp/Services/Navigation/FindSymbolsService.cs index 98ccf20803..105fefb438 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/Navigation/FindSymbolsService.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/Navigation/FindSymbolsService.cs @@ -23,13 +23,19 @@ public FindSymbolsService(OmniSharpWorkspace workspace) public async Task Handle(FindSymbolsRequest request = null) { + if (request?.Filter?.Length < request?.MinFilterLength.GetValueOrDefault()) + { + return new QuickFixResponse { QuickFixes = Array.Empty() }; + } + Func isMatch = candidate => request != null ? candidate.IsValidCompletionFor(request.Filter) : true; - var csprojSymbols = await _workspace.CurrentSolution.FindSymbols(isMatch, ".csproj"); - var projectJsonSymbols = await _workspace.CurrentSolution.FindSymbols(isMatch, ".json"); + int maxItemsToReturn = (request?.MaxItemsToReturn).GetValueOrDefault(); + var csprojSymbols = await _workspace.CurrentSolution.FindSymbols(isMatch, ".csproj", maxItemsToReturn); + var projectJsonSymbols = await _workspace.CurrentSolution.FindSymbols(isMatch, ".json", maxItemsToReturn); return new QuickFixResponse() { QuickFixes = csprojSymbols.QuickFixes.Concat(projectJsonSymbols.QuickFixes) diff --git a/src/OmniSharp.Roslyn/Extensions/SolutionExtensions.cs b/src/OmniSharp.Roslyn/Extensions/SolutionExtensions.cs index 53d30c877c..044b72e097 100644 --- a/src/OmniSharp.Roslyn/Extensions/SolutionExtensions.cs +++ b/src/OmniSharp.Roslyn/Extensions/SolutionExtensions.cs @@ -12,7 +12,8 @@ public static class SolutionExtensions { public static async Task FindSymbols(this Solution solution, Func predicate, - string projectFileExtension) + string projectFileExtension, + int maxItemsToReturn) { var projects = solution.Projects.Where(p => (p.FilePath?.EndsWith(projectFileExtension, StringComparison.OrdinalIgnoreCase) ?? false) || @@ -37,12 +38,27 @@ public static async Task FindSymbols(this Solution solution, { symbolLocations.Add(ConvertSymbol(solution, symbol, location)); } + + if (ShouldStopSearching(maxItemsToReturn, symbolLocations)) + { + break; + } + } + + if (ShouldStopSearching(maxItemsToReturn, symbolLocations)) + { + break; } } return new QuickFixResponse(symbolLocations.Distinct().ToList()); } + private static bool ShouldStopSearching(int maxItemsToReturn, List symbolLocations) + { + return maxItemsToReturn > 0 && symbolLocations.Count >= maxItemsToReturn; + } + private static QuickFix ConvertSymbol(Solution solution, ISymbol symbol, Location location) { var lineSpan = location.GetLineSpan(); diff --git a/tests/OmniSharp.Cake.Tests/FindSymbolsFacts.cs b/tests/OmniSharp.Cake.Tests/FindSymbolsFacts.cs index 7c9f7b79e0..78264701d4 100644 --- a/tests/OmniSharp.Cake.Tests/FindSymbolsFacts.cs +++ b/tests/OmniSharp.Cake.Tests/FindSymbolsFacts.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System.Linq; +using System.Threading.Tasks; using Microsoft.Extensions.Logging; using OmniSharp.Cake.Services.RequestHandlers.Navigation; using OmniSharp.Models; @@ -23,25 +24,41 @@ public FindSymbolsFacts(ITestOutputHelper testOutput) : base(testOutput) [Fact] public async Task ShouldFindSymbolsInCakeProjects() { - var symbols = await FindSymbols("CakeProject"); + var symbols = await FindSymbols("CakeProject", minFilterLength: null, maxItemsToReturn: null); Assert.NotEmpty(symbols.QuickFixes); } + [Fact] + public async Task ShouldNotFindSymbolsInCakeProjectsDueToEmptyFilter() + { + var symbols = await FindSymbols("CakeProject", minFilterLength: 1, maxItemsToReturn: 0); + Assert.Empty(symbols.QuickFixes); + } + + [Fact] + public async Task ShouldFindLimitedNumberOfSymbolsInCakeProjects() + { + var symbols = await FindSymbols("CakeProject", minFilterLength: 0, maxItemsToReturn: 100); + Assert.Equal(100, symbols.QuickFixes.Count()); + } + [Fact] public async Task ShouldNotFindSymbolsInCSharpProjects() { - var symbols = await FindSymbols("ProjectAndSolution"); + var symbols = await FindSymbols("ProjectAndSolution", minFilterLength: 0, maxItemsToReturn: 0); Assert.Empty(symbols.QuickFixes); } - private async Task FindSymbols(string projectName) + private async Task FindSymbols(string projectName, int? minFilterLength, int? maxItemsToReturn) { using (var testProject = await TestAssets.Instance.GetTestProjectAsync(projectName, shadowCopy : false)) using (var host = CreateOmniSharpHost(testProject.Directory)) { var request = new FindSymbolsRequest { - Filter = "" + Filter = "", + MinFilterLength = minFilterLength, + MaxItemsToReturn = maxItemsToReturn }; var requestHandler = GetRequestHandler(host); diff --git a/tests/OmniSharp.Roslyn.CSharp.Tests/FindSymbolsFacts.cs b/tests/OmniSharp.Roslyn.CSharp.Tests/FindSymbolsFacts.cs index c9462e2a79..61fd9234fa 100644 --- a/tests/OmniSharp.Roslyn.CSharp.Tests/FindSymbolsFacts.cs +++ b/tests/OmniSharp.Roslyn.CSharp.Tests/FindSymbolsFacts.cs @@ -215,7 +215,7 @@ private string NestedMethod() {} } }"; - var usages = await FindSymbolsWithFilterAsync(code, "meth"); + var usages = await FindSymbolsWithFilterAsync(code, "meth", minFilterLength: null, maxItemsToReturn: null); var symbols = usages.QuickFixes.Select(q => q.Text); var expected = new[] @@ -240,7 +240,7 @@ public interface IConfigurationOptions { } public class ConfigurationOptions : IConfigurationOptions { } }"; - var usages = await FindSymbolsWithFilterAsync(code, "opti"); + var usages = await FindSymbolsWithFilterAsync(code, "opti", minFilterLength: 0, maxItemsToReturn: 0); var symbols = usages.QuickFixes.Select(q => q.Text); var expected = new[] @@ -253,6 +253,37 @@ public class ConfigurationOptions : IConfigurationOptions { } Assert.Equal(expected, symbols); } + [Fact] + public async Task no_symbols_returned_when_filter_too_short() + { + const string code = @" + namespace Some.Namespace + { + public class Options {} + }"; + + var usages = await FindSymbolsWithFilterAsync(code, "op", minFilterLength: 3, maxItemsToReturn: 0); + var symbols = usages.QuickFixes.Select(q => q.Text); + + Assert.Empty(symbols); + } + + [Fact] + public async Task limit_number_of_returned_symbols() + { + const string code = @" + namespace Some.Namespace + { + public class Options1 {} + public class Options2 {} + public class Options3 {} + }"; + + var usages = await FindSymbolsWithFilterAsync(code, "op", minFilterLength: 0, maxItemsToReturn: 2); + var symbols = usages.QuickFixes.Select(q => q.Text); + + Assert.Equal(2, symbols.Count()); + } private async Task FindSymbolsAsync(string code) { @@ -263,13 +294,17 @@ private async Task FindSymbolsAsync(string code) return await requestHandler.Handle(null); } - private async Task FindSymbolsWithFilterAsync(string code, string filter) + private async Task FindSymbolsWithFilterAsync(string code, string filter, int? minFilterLength, int? maxItemsToReturn) { var testFile = new TestFile("dummy.cs", code); SharedOmniSharpTestHost.AddFilesToWorkspace(testFile); var requestHandler = GetRequestHandler(SharedOmniSharpTestHost); - return await requestHandler.Handle(new FindSymbolsRequest { Filter = filter }); + return await requestHandler.Handle(new FindSymbolsRequest { + Filter = filter, + MinFilterLength = minFilterLength, + MaxItemsToReturn = maxItemsToReturn + }); } } }