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

Commit 7d7ca94

Browse files
authored
Symbol indexer (#521)
For #433. Closes #483. Closes #484. Closes #485. General overview: - Indexing happens on each parse, so with the current LS, every file in the workspace gets indexed. This will need to be reworked. - All symbol queries go to the index, I have not yet approached #486. - I do some extra heuristics to highlight some of the more interesting symbols, namely: - `__init__` in a `class` will be marked as a constructor. - Functions annotated with a property decorator are given the kind `Property`. - Functions that fit the pattern `__.*__` are given the kind `Operator`. - Other functions in a class are given the kind `Method`. - Variables that look like `UPPERCASE_UNDERSCORE_123` at the top level or directly inside a class are given the kind `Constant`. - Any parse updates the index. I don't do any version management other than to look up the AST on the parse complete event. - VS-specific "functionKind" is preserved. Things to do in future PRs: - Fallback to analysis when it's present. I left all of the functions previously used there. - Using the import system to answer the question "is this thing being imported a module or not", probably augmented/modified outside the indexer. - Parse version management. - Asynchronous behavior (switching the dictionary to be a map from `Uri` to `TaskCompletionSource<HierarchicalSymbol>`).
1 parent 23f6d67 commit 7d7ca94

File tree

10 files changed

+1381
-32
lines changed

10 files changed

+1381
-32
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Python Tools for Visual Studio
2+
// Copyright(c) Microsoft Corporation
3+
// All rights reserved.
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the License); you may not use
6+
// this file except in compliance with the License. You may obtain a copy of the
7+
// License at http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
10+
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
11+
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
12+
// MERCHANTABLITY OR NON-INFRINGEMENT.
13+
//
14+
// See the Apache Version 2.0 License for specific language governing
15+
// permissions and limitations under the License.
16+
17+
using System;
18+
using System.Collections.Generic;
19+
20+
namespace Microsoft.PythonTools.Analysis.Indexing {
21+
internal interface ISymbolIndex {
22+
IEnumerable<HierarchicalSymbol> HierarchicalDocumentSymbols(Uri uri);
23+
24+
IEnumerable<FlatSymbol> WorkspaceSymbols(string query);
25+
}
26+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Python Tools for Visual Studio
2+
// Copyright(c) Microsoft Corporation
3+
// All rights reserved.
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the License); you may not use
6+
// this file except in compliance with the License. You may obtain a copy of the
7+
// License at http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
10+
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
11+
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
12+
// MERCHANTABLITY OR NON-INFRINGEMENT.
13+
//
14+
// See the Apache Version 2.0 License for specific language governing
15+
// permissions and limitations under the License.
16+
17+
using System;
18+
using System.Collections.Generic;
19+
20+
namespace Microsoft.PythonTools.Analysis.Indexing {
21+
internal static class SymbolExtensions {
22+
public static IEnumerable<FlatSymbol> Flatten(this IEnumerable<HierarchicalSymbol> docSyms, Uri uri, string parent = null, int? depthLimit = null) {
23+
foreach (var sym in docSyms) {
24+
yield return new FlatSymbol(sym.Name, sym.Kind, uri, sym.SelectionRange, parent);
25+
26+
if (depthLimit != null) {
27+
if (depthLimit < 1) {
28+
yield break;
29+
}
30+
depthLimit--;
31+
}
32+
33+
foreach (var si in sym.Children.Flatten(uri, sym.Name, depthLimit)) {
34+
yield return si;
35+
}
36+
}
37+
}
38+
}
39+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Python Tools for Visual Studio
2+
// Copyright(c) Microsoft Corporation
3+
// All rights reserved.
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the License); you may not use
6+
// this file except in compliance with the License. You may obtain a copy of the
7+
// License at http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
10+
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
11+
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
12+
// MERCHANTABLITY OR NON-INFRINGEMENT.
13+
//
14+
// See the Apache Version 2.0 License for specific language governing
15+
// permissions and limitations under the License.
16+
17+
using System;
18+
using System.Collections.Concurrent;
19+
using System.Collections.Generic;
20+
using System.Linq;
21+
using Microsoft.PythonTools.Analysis.Infrastructure;
22+
using Microsoft.PythonTools.Parsing.Ast;
23+
24+
namespace Microsoft.PythonTools.Analysis.Indexing {
25+
internal class SymbolIndex : ISymbolIndex {
26+
private readonly ConcurrentDictionary<Uri, IReadOnlyList<HierarchicalSymbol>> _index = new ConcurrentDictionary<Uri, IReadOnlyList<HierarchicalSymbol>>();
27+
28+
public SymbolIndex() { }
29+
30+
public IEnumerable<HierarchicalSymbol> HierarchicalDocumentSymbols(Uri uri)
31+
=> _index.TryGetValue(uri, out var list) ? list : Enumerable.Empty<HierarchicalSymbol>();
32+
33+
public IEnumerable<FlatSymbol> WorkspaceSymbols(string query) {
34+
foreach (var kvp in _index) {
35+
foreach (var found in WorkspaceSymbolsQuery(query, kvp.Key, kvp.Value)) {
36+
yield return found;
37+
}
38+
}
39+
}
40+
41+
private IEnumerable<FlatSymbol> WorkspaceSymbolsQuery(string query, Uri uri, IEnumerable<HierarchicalSymbol> symbols) {
42+
// Some semblance of a BFS.
43+
var queue = new Queue<(HierarchicalSymbol, string)>(symbols.Select(s => (s, (string)null)));
44+
45+
while (queue.Count > 0) {
46+
var (sym, parent) = queue.Dequeue();
47+
48+
if (sym.Name.ContainsOrdinal(query, ignoreCase: true)) {
49+
yield return new FlatSymbol(sym.Name, sym.Kind, uri, sym.SelectionRange, parent);
50+
}
51+
52+
foreach (var child in sym.Children.MaybeEnumerate()) {
53+
queue.Enqueue((child, sym.Name));
54+
}
55+
}
56+
}
57+
58+
public void UpdateParseTree(Uri uri, PythonAst ast) {
59+
var walker = new SymbolIndexWalker(ast);
60+
ast.Walk(walker);
61+
_index[uri] = walker.Symbols;
62+
}
63+
}
64+
}

0 commit comments

Comments
 (0)