diff --git a/src/Analysis/Engine/Impl/Infrastructure/Extensions/EnumerableExtensions.cs b/src/Analysis/Engine/Impl/Infrastructure/Extensions/EnumerableExtensions.cs index 33b5855f0..b683a100a 100644 --- a/src/Analysis/Engine/Impl/Infrastructure/Extensions/EnumerableExtensions.cs +++ b/src/Analysis/Engine/Impl/Infrastructure/Extensions/EnumerableExtensions.cs @@ -59,5 +59,24 @@ public static bool SetEquals(this IEnumerable source, IEnumerable other public static IEnumerable Keys(this IEnumerable> source) => source.Select(GetKey); public static IEnumerable ExcludeDefault(this IEnumerable source) => source.Where(i => !Equals(i, default(T))); + + + public static IEnumerable TraverseBreadthFirst(this T root, Func> selectChildren) { + var items = new Queue(); + items.Enqueue(root); + while (items.Count > 0) { + var item = items.Dequeue(); + yield return item; + + var childen = selectChildren(item); + if (childen == null) { + continue; + } + + foreach (var child in childen) { + items.Enqueue(child); + } + } + } } } diff --git a/src/Analysis/Engine/Impl/Values/Protocols.cs b/src/Analysis/Engine/Impl/Values/Protocols.cs index 8a095c638..def71b7d7 100644 --- a/src/Analysis/Engine/Impl/Values/Protocols.cs +++ b/src/Analysis/Engine/Impl/Values/Protocols.cs @@ -385,10 +385,13 @@ protected override Protocol UnionMergeTypes(Protocol p) { } class TupleProtocol : IterableProtocol { - internal readonly IAnalysisSet[] _values; + private readonly IAnalysisSet[] _values; public TupleProtocol(ProtocolInfo self, IEnumerable values) : base(self, AnalysisSet.UnionAll(values)) { _values = values.Select(s => s.AsUnion(1)).ToArray(); + + var argTypes = _values.SelectMany(e => e.Select(v => v is IHasQualifiedName qn ? qn.FullyQualifiedName : v.ShortDescription)); + Name = "tuple[{0}]".FormatInvariant(string.Join(", ", argTypes)); } protected override void EnsureMembers(IDictionary members) { @@ -415,7 +418,7 @@ public override IAnalysisSet GetIndex(Node node, AnalysisUnit unit, IAnalysisSet return AnalysisSet.UnionAll(constants.Select(GetItem)); } - public override string Name => "tuple"; + public override string Name { get; } public override IEnumerable> GetRichDescription() { if (_values.Any()) { diff --git a/src/LanguageServer/Impl/Definitions/ServerSettings.cs b/src/LanguageServer/Impl/Definitions/ServerSettings.cs index 88fc757a9..0b519fc97 100644 --- a/src/LanguageServer/Impl/Definitions/ServerSettings.cs +++ b/src/LanguageServer/Impl/Definitions/ServerSettings.cs @@ -25,6 +25,7 @@ public class PythonAnalysisOptions { public bool openFilesOnly; public int symbolsHierarchyDepthLimit = 10; + public int symbolsHierarchyMaxSymbols = 1000; public string[] errors { get; } = Array.Empty(); public string[] warnings { get; } = Array.Empty(); diff --git a/src/LanguageServer/Impl/Implementation/Server.WorkspaceSymbols.cs b/src/LanguageServer/Impl/Implementation/Server.WorkspaceSymbols.cs index 23df611ed..a23156b14 100644 --- a/src/LanguageServer/Impl/Implementation/Server.WorkspaceSymbols.cs +++ b/src/LanguageServer/Impl/Implementation/Server.WorkspaceSymbols.cs @@ -14,6 +14,7 @@ // See the Apache Version 2.0 License for specific language governing // permissions and limitations under the License. +using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -26,7 +27,8 @@ namespace Microsoft.Python.LanguageServer.Implementation { public sealed partial class Server { - private static int _symbolHierarchyDepthLimit = 1; + private static int _symbolHierarchyDepthLimit = 10; + private static int _symbolHierarchyMaxSymbols = 1000; public override async Task WorkspaceSymbols(WorkspaceSymbolParams @params, CancellationToken cancellationToken) { await WaitForCompleteAnalysisAsync(cancellationToken); @@ -72,8 +74,9 @@ private static async Task> GetModuleVariablesAsync(ProjectEn } private static IEnumerable GetModuleVariables(ProjectEntry entry, GetMemberOptions opts, string prefix, IModuleAnalysis analysis) { - var all = analysis.GetAllMembers(SourceLocation.None, opts); - return all + var breadthFirst = analysis.Scope.TraverseBreadthFirst(s => s.Children); + var all = breadthFirst.SelectMany(c => analysis.GetAllAvailableMembersFromScope(c, opts)); + var result = all .Where(m => { if (m.Values.Any(v => v.DeclaringModule == entry || v.Locations.Any(l => l.DocumentUri == entry.DocumentUri))) { if (string.IsNullOrEmpty(prefix) || m.Name.StartsWithOrdinal(prefix, ignoreCase: true)) { @@ -82,17 +85,10 @@ private static IEnumerable GetModuleVariables(ProjectEntry entry, } return false; }) - .Concat(GetChildScopesVariables(analysis, analysis.Scope, opts, 0)); + .Take(_symbolHierarchyMaxSymbols); + return result; } - private static IEnumerable GetChildScopesVariables(IModuleAnalysis analysis, IScope scope, GetMemberOptions opts, int currentDepth) - => currentDepth < _symbolHierarchyDepthLimit - ? scope.Children.SelectMany(c => GetScopeVariables(analysis, c, opts, currentDepth)) - : Enumerable.Empty(); - - private static IEnumerable GetScopeVariables(IModuleAnalysis analysis, IScope scope, GetMemberOptions opts, int currentDepth) - => analysis.GetAllAvailableMembersFromScope(scope, opts).Concat(GetChildScopesVariables(analysis, scope, opts, currentDepth + 1)); - private SymbolInformation ToSymbolInformation(IMemberResult m) { var res = new SymbolInformation { name = m.Name, @@ -115,8 +111,8 @@ private SymbolInformation ToSymbolInformation(IMemberResult m) { } private DocumentSymbol[] ToDocumentSymbols(List members) { - var childMap = new Dictionary>(); var topLevel = new List(); + var childMap = new Dictionary>(); foreach (var m in members) { var parent = members.FirstOrDefault(x => x.Scope?.Node == m.Scope?.OuterScope?.Node && x.Name == m.Scope?.Name); @@ -131,10 +127,10 @@ private DocumentSymbol[] ToDocumentSymbols(List members) { } var symbols = topLevel - .GroupBy(mr => mr.Name) - .Select(g => g.First()) - .Select(m => ToDocumentSymbol(m, childMap, 0)) - .ToArray(); + .GroupBy(mr => mr.Name) + .Select(g => g.First()) + .Select(m => ToDocumentSymbol(m, childMap, 0)) + .ToArray(); return symbols; } @@ -149,9 +145,11 @@ private DocumentSymbol ToDocumentSymbol(IMemberResult m, Dictionary ToDocumentSymbol(x, childMap, currentDepth + 1)).ToArray(); + res.children = children + .Select(x => ToDocumentSymbol(x, childMap, currentDepth + 1)) + .ToArray(); } else { - res.children = new DocumentSymbol[0]; + res.children = Array.Empty(); } var loc = m.Locations.FirstOrDefault(l => !string.IsNullOrEmpty(l.FilePath)); diff --git a/src/LanguageServer/Impl/Implementation/Server.cs b/src/LanguageServer/Impl/Implementation/Server.cs index fe041a71c..b48c232dd 100644 --- a/src/LanguageServer/Impl/Implementation/Server.cs +++ b/src/LanguageServer/Impl/Implementation/Server.cs @@ -685,6 +685,7 @@ private bool HandleConfigurationChanges(ServerSettings newSettings) { Settings = newSettings; _symbolHierarchyDepthLimit = Settings.analysis.symbolsHierarchyDepthLimit; + _symbolHierarchyMaxSymbols = Settings.analysis.symbolsHierarchyMaxSymbols; if (oldSettings == null) { return true; diff --git a/src/LanguageServer/Impl/LanguageServer.cs b/src/LanguageServer/Impl/LanguageServer.cs index 425475bba..21ce4110f 100644 --- a/src/LanguageServer/Impl/LanguageServer.cs +++ b/src/LanguageServer/Impl/LanguageServer.cs @@ -143,6 +143,7 @@ public async Task DidChangeConfiguration(JToken token, CancellationToken cancell settings.analysis.openFilesOnly = GetSetting(analysis, "openFilesOnly", false); settings.diagnosticPublishDelay = GetSetting(analysis, "diagnosticPublishDelay", 1000); settings.symbolsHierarchyDepthLimit = GetSetting(analysis, "symbolsHierarchyDepthLimit", 10); + settings.symbolsHierarchyMaxSymbols = GetSetting(analysis, "symbolsHierarchyMaxSymbols", 1000); _ui.SetLogLevel(GetLogLevel(analysis)); diff --git a/src/LanguageServer/Impl/LanguageServerSettings.cs b/src/LanguageServer/Impl/LanguageServerSettings.cs index d965ec9eb..0cc6917ed 100644 --- a/src/LanguageServer/Impl/LanguageServerSettings.cs +++ b/src/LanguageServer/Impl/LanguageServerSettings.cs @@ -18,5 +18,6 @@ namespace Microsoft.Python.LanguageServer.Implementation { public sealed class LanguageServerSettings: ServerSettings { public int diagnosticPublishDelay = 1000; public int symbolsHierarchyDepthLimit = 10; + public int symbolsHierarchyMaxSymbols = 1000; } }