Skip to content
This repository was archived by the owner on Nov 4, 2024. It is now read-only.

Commit c10b41a

Browse files
authored
Async Indexer for new LanguageServer (microsoft#558)
Adding back SymbolIndex to new LanguageServer
1 parent 8b049da commit c10b41a

29 files changed

+2628
-86
lines changed

src/Analysis/Ast/Impl/Microsoft.Python.Analysis.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
<PrivateAssets>all</PrivateAssets>
1919
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
2020
</PackageReference>
21+
<PackageReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="2.2.0" />
2122
</ItemGroup>
2223
<ItemGroup>
2324
<Content Include="get_search_paths.py">

src/Core/Impl/Extensions/EnumerableExtensions.cs

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,30 @@ public static IEnumerable<int> IndexWhere<T>(this IEnumerable<T> source, Func<T,
8888
i++;
8989
}
9090
}
91+
92+
public static IEnumerable<T> TraverseBreadthFirst<T>(this T root, Func<T, IEnumerable<T>> selectChildren)
93+
=> Enumerable.Repeat(root, 1).TraverseBreadthFirst(selectChildren);
94+
95+
public static IEnumerable<T> TraverseBreadthFirst<T>(this IEnumerable<T> roots, Func<T, IEnumerable<T>> selectChildren) {
96+
var items = new Queue<T>(roots);
97+
while (items.Count > 0) {
98+
var item = items.Dequeue();
99+
yield return item;
100+
101+
var children = selectChildren(item);
102+
if (children == null) {
103+
continue;
104+
}
105+
106+
foreach (var child in children) {
107+
items.Enqueue(child);
108+
}
109+
}
110+
}
91111

92112
public static Dictionary<TKey, TValue> ToDictionary<TSource, TKey, TValue>(this IEnumerable<TSource> source, Func<TSource, int, TKey> keySelector, Func<TSource, int, TValue> valueSelector) {
93-
var dictionary = source is IReadOnlyCollection<TSource> collection
94-
? new Dictionary<TKey, TValue>(collection.Count)
113+
var dictionary = source is IReadOnlyCollection<TSource> collection
114+
? new Dictionary<TKey, TValue>(collection.Count)
95115
: new Dictionary<TKey, TValue>();
96116

97117
var index = 0;
@@ -130,23 +150,5 @@ public static IEnumerable<T> TraverseDepthFirst<T>(this T root, Func<T, IEnumera
130150
reverseChildren.Clear();
131151
}
132152
}
133-
134-
public static IEnumerable<T> TraverseBreadthFirst<T>(this T root, Func<T, IEnumerable<T>> selectChildren) {
135-
var items = new Queue<T>();
136-
items.Enqueue(root);
137-
while (items.Count > 0) {
138-
var item = items.Dequeue();
139-
yield return item;
140-
141-
var children = selectChildren(item);
142-
if (children == null) {
143-
continue;
144-
}
145-
146-
foreach (var child in children) {
147-
items.Enqueue(child);
148-
}
149-
}
150-
}
151153
}
152154
}

src/Core/Impl/Extensions/StringExtensions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,8 @@ public static bool PathEquals(this string s, string other)
206206

207207
public static bool EqualsOrdinal(this string s, int index, string other, int otherIndex, int length, bool ignoreCase = false)
208208
=> string.Compare(s, index, other, otherIndex, length, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal) == 0;
209+
public static bool ContainsOrdinal(this string s, string value, bool ignoreCase = false)
210+
=> s.IndexOfOrdinal(value, ignoreCase: ignoreCase) != -1;
209211

210212
public static int GetPathHashCode(this string s)
211213
=> IgnoreCaseInPaths ? StringComparer.OrdinalIgnoreCase.GetHashCode(s) : StringComparer.Ordinal.GetHashCode(s);

src/Core/Impl/Extensions/TaskExtensions.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@
2121

2222
namespace Microsoft.Python.Core {
2323
public static class TaskExtensions {
24-
public static void SetCompletionResultTo<T>(this Task<T> task, TaskCompletionSourceEx<T> tcs)
24+
public static void SetCompletionResultTo<T>(this Task<T> task, TaskCompletionSource<T> tcs)
2525
=> task.ContinueWith(SetCompletionResultToContinuation, tcs, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
26-
26+
2727
private static void SetCompletionResultToContinuation<T>(Task<T> task, object state) {
28-
var tcs = (TaskCompletionSourceEx<T>) state;
28+
var tcs = (TaskCompletionSource<T>)state;
2929
switch (task.Status) {
3030
case TaskStatus.RanToCompletion:
3131
tcs.TrySetResult(task.Result);
@@ -34,7 +34,7 @@ private static void SetCompletionResultToContinuation<T>(Task<T> task, object st
3434
try {
3535
task.GetAwaiter().GetResult();
3636
} catch (OperationCanceledException ex) {
37-
tcs.TrySetCanceled(ex);
37+
tcs.TrySetCanceled(ex.CancellationToken);
3838
}
3939
break;
4040
case TaskStatus.Faulted:

src/Core/Impl/IO/DirectoryInfoProxy.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@
1313
// See the Apache Version 2.0 License for specific language governing
1414
// permissions and limitations under the License.
1515

16+
using System;
1617
using System.Collections.Generic;
1718
using System.IO;
1819
using System.Linq;
20+
using Microsoft.Extensions.FileSystemGlobbing;
21+
using Microsoft.Extensions.FileSystemGlobbing.Abstractions;
1922

2023
namespace Microsoft.Python.Core.IO {
2124
public sealed class DirectoryInfoProxy : IDirectoryInfo {
@@ -40,6 +43,39 @@ public IEnumerable<IFileSystemInfo> EnumerateFileSystemInfos() => _directoryInfo
4043
.EnumerateFileSystemInfos()
4144
.Select(CreateFileSystemInfoProxy);
4245

46+
public IEnumerable<IFileSystemInfo> EnumerateFileSystemInfos(string[] includePatterns, string[] excludePatterns) {
47+
var matcher = GetMatcher(includePatterns, excludePatterns);
48+
PatternMatchingResult matchResult = SafeExecuteMatcher(matcher);
49+
return matchResult.Files.Select((filePatternMatch) => {
50+
var fileSystemInfo = _directoryInfo.GetFileSystemInfos(filePatternMatch.Stem).First();
51+
return CreateFileSystemInfoProxy(fileSystemInfo);
52+
});
53+
}
54+
55+
public bool Match(string path, string[] includePatterns = default, string[] excludePatterns = default) {
56+
var matcher = GetMatcher(includePatterns, excludePatterns);
57+
return matcher.Match(FullName, path).HasMatches;
58+
}
59+
60+
private static Matcher GetMatcher(string[] includePatterns, string[] excludePatterns) {
61+
Matcher matcher = new Matcher();
62+
matcher.AddIncludePatterns(includePatterns.IsNullOrEmpty() ? new[] { "**/*" } : includePatterns);
63+
if (!excludePatterns.IsNullOrEmpty()) {
64+
matcher.AddExcludePatterns(excludePatterns);
65+
}
66+
return matcher;
67+
}
68+
69+
private PatternMatchingResult SafeExecuteMatcher(Matcher matcher) {
70+
var directoryInfo = new DirectoryInfoWrapper(_directoryInfo);
71+
for (var retries = 5; retries > 0; retries--) {
72+
try {
73+
return matcher.Execute(directoryInfo);
74+
} catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException) { }
75+
}
76+
return new PatternMatchingResult(Enumerable.Empty<FilePatternMatch>());
77+
}
78+
4379
private static IFileSystemInfo CreateFileSystemInfoProxy(FileSystemInfo fileSystemInfo)
4480
=> fileSystemInfo is DirectoryInfo directoryInfo
4581
? (IFileSystemInfo)new DirectoryInfoProxy(directoryInfo)

src/Core/Impl/IO/FileSystem.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public long FileSize(string path) {
3232
public void FileWriteAllBytes(string path, byte[] bytes) => File.WriteAllBytes(path, bytes);
3333
public Stream CreateFile(string path) => File.Create(path);
3434
public Stream FileOpen(string path, FileMode mode) => File.Open(path, mode);
35+
public Stream FileOpen(string path, FileMode mode, FileAccess access, FileShare share) => File.Open(path, mode, access, share);
3536
public bool DirectoryExists(string path) => Directory.Exists(path);
3637
public FileAttributes GetFileAttributes(string path) => File.GetAttributes(path);
3738
public void SetFileAttributes(string fullPath, FileAttributes attributes) => File.SetAttributes(fullPath, attributes);

src/Core/Impl/IO/IDirectoryInfo.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,7 @@ namespace Microsoft.Python.Core.IO {
1919
public interface IDirectoryInfo : IFileSystemInfo {
2020
IDirectoryInfo Parent { get; }
2121
IEnumerable<IFileSystemInfo> EnumerateFileSystemInfos();
22+
IEnumerable<IFileSystemInfo> EnumerateFileSystemInfos(string[] includeFiles, string[] excludeFiles);
23+
bool Match(string path, string[] includePatterns = default, string[] excludePatterns = default);
2224
}
2325
}

src/Core/Impl/IO/IFileSystem.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public interface IFileSystem {
4141

4242
Stream CreateFile(string path);
4343
Stream FileOpen(string path, FileMode mode);
44+
Stream FileOpen(string path, FileMode mode, FileAccess access, FileShare share);
4445

4546
Version GetFileVersion(string path);
4647
void DeleteFile(string path);

src/Core/Impl/Microsoft.Python.Core.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
<PrivateAssets>all</PrivateAssets>
2424
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
2525
</PackageReference>
26+
<PackageReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="2.2.0" />
2627
</ItemGroup>
2728
<ItemGroup>
2829
<Compile Update="Resources.Designer.cs">

src/LanguageServer/Impl/Implementation/Server.Documents.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,17 @@ namespace Microsoft.Python.LanguageServer.Implementation {
2727
public sealed partial class Server {
2828
public void DidOpenTextDocument(DidOpenTextDocumentParams @params) {
2929
_disposableBag.ThrowIfDisposed();
30-
_log?.Log(TraceEventType.Verbose, $"Opening document {@params.textDocument.uri}");
30+
var uri = @params.textDocument.uri;
31+
_log?.Log(TraceEventType.Verbose, $"Opening document {uri}");
3132

32-
_rdt.OpenDocument(@params.textDocument.uri, @params.textDocument.text);
33+
var doc = _rdt.OpenDocument(uri, @params.textDocument.text);
34+
_indexManager.ProcessNewFile(uri.AbsolutePath, doc);
3335
}
3436

3537
public void DidChangeTextDocument(DidChangeTextDocumentParams @params) {
3638
_disposableBag.ThrowIfDisposed();
37-
var doc = _rdt.GetDocument(@params.textDocument.uri);
39+
var uri = @params.textDocument.uri;
40+
var doc = _rdt.GetDocument(uri);
3841
if (doc != null) {
3942
var changes = new List<DocumentChange>();
4043
foreach (var c in @params.contentChanges) {
@@ -46,6 +49,7 @@ public void DidChangeTextDocument(DidChangeTextDocumentParams @params) {
4649
changes.Add(change);
4750
}
4851
doc.Update(changes);
52+
_indexManager.AddPendingDoc(doc);
4953
} else {
5054
_log?.Log(TraceEventType.Warning, $"Unable to find document for {@params.textDocument.uri}");
5155
}
@@ -60,7 +64,9 @@ public void DidChangeWatchedFiles(DidChangeWatchedFilesParams @params) {
6064

6165
public void DidCloseTextDocument(DidCloseTextDocumentParams @params) {
6266
_disposableBag.ThrowIfDisposed();
63-
_rdt.CloseDocument(@params.textDocument.uri);
67+
var uri = @params.textDocument.uri;
68+
_rdt.CloseDocument(uri);
69+
_indexManager.ProcessClosedFile(uri.AbsolutePath);
6470
}
6571

6672
private Task<IDocumentAnalysis> GetAnalysisAsync(Uri uri, CancellationToken cancellationToken) {
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// Copyright(c) Microsoft Corporation
2+
// All rights reserved.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the License); you may not use
5+
// this file except in compliance with the License. You may obtain a copy of the
6+
// License at http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
9+
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
10+
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
11+
// MERCHANTABILITY OR NON-INFRINGEMENT.
12+
//
13+
// See the Apache Version 2.0 License for specific language governing
14+
// permissions and limitations under the License.
15+
16+
using System;
17+
using System.Linq;
18+
using System.Threading;
19+
using System.Threading.Tasks;
20+
using Microsoft.Python.Core;
21+
using Microsoft.Python.LanguageServer.Indexing;
22+
using Microsoft.Python.LanguageServer.Protocol;
23+
24+
namespace Microsoft.Python.LanguageServer.Implementation {
25+
public sealed partial class Server {
26+
private static int _symbolHierarchyMaxSymbols = 1000;
27+
28+
public async Task<SymbolInformation[]> WorkspaceSymbols(WorkspaceSymbolParams @params, CancellationToken cancellationToken) {
29+
var symbols = await _indexManager.WorkspaceSymbolsAsync(@params.query,
30+
_symbolHierarchyMaxSymbols,
31+
cancellationToken);
32+
return symbols.Select(MakeSymbolInfo).ToArray();
33+
}
34+
35+
public async Task<DocumentSymbol[]> HierarchicalDocumentSymbol(DocumentSymbolParams @params, CancellationToken cancellationToken) {
36+
var path = @params.textDocument.uri.AbsolutePath;
37+
var symbols = await _indexManager.HierarchicalDocumentSymbolsAsync(path, cancellationToken);
38+
cancellationToken.ThrowIfCancellationRequested();
39+
return symbols.Select(hSym => MakeDocumentSymbol(hSym)).ToArray();
40+
}
41+
42+
private static SymbolInformation MakeSymbolInfo(FlatSymbol s) {
43+
return new SymbolInformation {
44+
name = s.Name,
45+
kind = (Protocol.SymbolKind)s.Kind,
46+
location = new Location {
47+
range = s.Range,
48+
uri = new Uri(s.DocumentPath),
49+
},
50+
containerName = s.ContainerName,
51+
};
52+
}
53+
54+
private DocumentSymbol MakeDocumentSymbol(HierarchicalSymbol hSym) {
55+
return new DocumentSymbol {
56+
name = hSym.Name,
57+
detail = hSym.Detail,
58+
kind = ToSymbolKind(hSym.Kind),
59+
deprecated = hSym.Deprecated ?? false,
60+
range = hSym.Range,
61+
selectionRange = hSym.SelectionRange,
62+
children = hSym.Children.MaybeEnumerate().Select(MakeDocumentSymbol).ToArray(),
63+
};
64+
}
65+
66+
private Protocol.SymbolKind ToSymbolKind(Indexing.SymbolKind kind) {
67+
switch (kind) {
68+
case Indexing.SymbolKind.None:
69+
return Protocol.SymbolKind.None;
70+
case Indexing.SymbolKind.File:
71+
return Protocol.SymbolKind.File;
72+
case Indexing.SymbolKind.Module:
73+
return Protocol.SymbolKind.Module;
74+
case Indexing.SymbolKind.Namespace:
75+
return Protocol.SymbolKind.Namespace;
76+
case Indexing.SymbolKind.Package:
77+
return Protocol.SymbolKind.Package;
78+
case Indexing.SymbolKind.Class:
79+
return Protocol.SymbolKind.Class;
80+
case Indexing.SymbolKind.Method:
81+
return Protocol.SymbolKind.Method;
82+
case Indexing.SymbolKind.Property:
83+
return Protocol.SymbolKind.Property;
84+
case Indexing.SymbolKind.Field:
85+
return Protocol.SymbolKind.Field;
86+
case Indexing.SymbolKind.Constructor:
87+
return Protocol.SymbolKind.Constructor;
88+
case Indexing.SymbolKind.Enum:
89+
return Protocol.SymbolKind.Enum;
90+
case Indexing.SymbolKind.Interface:
91+
return Protocol.SymbolKind.Interface;
92+
case Indexing.SymbolKind.Function:
93+
return Protocol.SymbolKind.Function;
94+
case Indexing.SymbolKind.Variable:
95+
return Protocol.SymbolKind.Variable;
96+
case Indexing.SymbolKind.Constant:
97+
return Protocol.SymbolKind.Constant;
98+
case Indexing.SymbolKind.String:
99+
return Protocol.SymbolKind.String;
100+
case Indexing.SymbolKind.Number:
101+
return Protocol.SymbolKind.Number;
102+
case Indexing.SymbolKind.Boolean:
103+
return Protocol.SymbolKind.Boolean;
104+
case Indexing.SymbolKind.Array:
105+
return Protocol.SymbolKind.Array;
106+
case Indexing.SymbolKind.Object:
107+
return Protocol.SymbolKind.Object;
108+
case Indexing.SymbolKind.Key:
109+
return Protocol.SymbolKind.Key;
110+
case Indexing.SymbolKind.Null:
111+
return Protocol.SymbolKind.Null;
112+
case Indexing.SymbolKind.EnumMember:
113+
return Protocol.SymbolKind.EnumMember;
114+
case Indexing.SymbolKind.Struct:
115+
return Protocol.SymbolKind.Struct;
116+
case Indexing.SymbolKind.Event:
117+
return Protocol.SymbolKind.Event;
118+
case Indexing.SymbolKind.Operator:
119+
return Protocol.SymbolKind.Operator;
120+
case Indexing.SymbolKind.TypeParameter:
121+
return Protocol.SymbolKind.TypeParameter;
122+
default:
123+
throw new NotImplementedException($"{kind} is not a LSP's SymbolKind");
124+
}
125+
}
126+
}
127+
}

0 commit comments

Comments
 (0)