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

Commit 32a1378

Browse files
- Various import completion fixes, import search now is always happening through PathResolver (microsoft#722)
- Filtering empty name at the end of module path
1 parent 0ec54ba commit 32a1378

File tree

4 files changed

+192
-43
lines changed

4 files changed

+192
-43
lines changed

src/Analysis/Core/Impl/DependencyResolution/PathResolverSnapshot.Node.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,9 @@ public ImmutableArray<string> GetChildrenNames() {
119119
foreach (var edge in _edges) {
120120
var children = edge.End.Children;
121121
foreach (var child in children) {
122-
results = results.Add(child.Name);
122+
if (IsNotInitPy(child)) {
123+
results = results.Add(child.Name);
124+
}
123125
}
124126
}
125127

src/Analysis/Core/Impl/DependencyResolution/PathResolverSnapshot.cs

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ public IImportSearchResult GetImportsFromAbsoluteName(in string modulePath, in I
135135
lastEdge = default;
136136
}
137137

138-
var fullNameList = fullName.ToList();
138+
var fullNameList = fullName.TakeWhile(n => !string.IsNullOrEmpty(n)).ToList();
139139
if (fullNameList.Count == 1 && lastEdge.IsNonRooted && TryFindNonRootedModule(fullNameList[0], out var moduleImport)) {
140140
return moduleImport;
141141
}
@@ -195,7 +195,7 @@ public IImportSearchResult GetImportsFromRelativePath(in string modulePath, in i
195195
return default;
196196
}
197197

198-
var fullNameList = relativePath.ToList();
198+
var fullNameList = relativePath.TakeWhile(n => !string.IsNullOrEmpty(n)).ToList();
199199
if (lastEdge.IsNonRooted) {
200200
// Handle relative imports only for modules in the same folder
201201
if (parentCount > 1) {
@@ -215,9 +215,13 @@ public IImportSearchResult GetImportsFromRelativePath(in string modulePath, in i
215215
var relativeParentEdge = lastEdge.GetPrevious(parentCount);
216216

217217
var rootEdges = new List<Edge>();
218-
for (var i = 0; i < _roots.Count; i++) {
219-
if (RootContains(i, relativeParentEdge, out var rootEdge)) {
220-
rootEdges.Add(rootEdge);
218+
if (relativeParentEdge.IsFirst) {
219+
rootEdges.Add(relativeParentEdge);
220+
} else {
221+
for (var i = 0; i < _roots.Count; i++) {
222+
if (RootContains(i, relativeParentEdge, out var rootEdge)) {
223+
rootEdges.Add(rootEdge);
224+
}
221225
}
222226
}
223227

@@ -296,14 +300,18 @@ private bool TryFindNonRootedModule(string moduleName, out ModuleImport moduleIm
296300
}
297301

298302
private bool TryCreateNamespacePackageImports(in ImmutableArray<Edge> matchedEdges, out IImportSearchResult searchResult) {
299-
if (matchedEdges.Count == 0) {
300-
searchResult = default;
301-
return false;
303+
foreach (var edge in matchedEdges) {
304+
if (edge.IsFirst) {
305+
continue;
306+
}
307+
308+
var importNode = edge.End;
309+
searchResult = new ImplicitPackageImport(new ChildrenSource(this, matchedEdges), importNode.Name, importNode.FullModuleName);
310+
return true;
302311
}
303312

304-
var importNode = matchedEdges[0].End;
305-
searchResult = new ImplicitPackageImport(new ChildrenSource(this, matchedEdges), importNode.Name, importNode.FullModuleName);
306-
return true;
313+
searchResult = default;
314+
return false;
307315
}
308316

309317
public PathResolverSnapshot SetWorkDirectory(in string workDirectory, out IEnumerable<string> addedRoots) {
@@ -751,6 +759,9 @@ private static int GetModuleNameEnd(string rootedModulePath)
751759
private static bool IsNotInitPy(string name)
752760
=> !name.EqualsOrdinal("__init__");
753761

762+
private static bool IsNotInitPy(in Node node)
763+
=> !node.IsModule || !node.Name.EqualsOrdinal("__init__");
764+
754765
private static bool IsInitPyModule(in Node node, out Node initPyNode)
755766
=> node.TryGetChild("__init__", out initPyNode) && initPyNode.IsModule;
756767

src/LanguageServer/Impl/Completion/ImportCompletion.cs

Lines changed: 54 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
using System.IO;
1919
using System.Linq;
2020
using Microsoft.Python.Analysis.Core.DependencyResolution;
21+
using Microsoft.Python.Analysis.Documents;
2122
using Microsoft.Python.Analysis.Types;
23+
using Microsoft.Python.Core;
2224
using Microsoft.Python.Core.Collections;
2325
using Microsoft.Python.Core.Text;
2426
using Microsoft.Python.LanguageServer.Protocol;
@@ -32,18 +34,27 @@ public static CompletionResult TryGetCompletions(ImportStatement import, Complet
3234
return new CompletionResult(GetAllImportableModules(context));
3335
}
3436

35-
foreach (var (name, asName) in ZipLongest(import.Names, import.AsNames).Reverse()) {
36-
if (asName != null && context.Position >= asName.StartIndex) {
37+
for (var i = import.Names.Count - 1; i >= 0; i--) {
38+
if (import.AsNames.Count > i && import.AsNames[i] != null && context.Position >= import.AsNames[i].StartIndex) {
3739
return CompletionResult.Empty;
3840
}
3941

42+
var name = import.Names[i];
4043
if (name != null && context.Position >= name.StartIndex) {
4144
if (context.Position > name.EndIndex && name.EndIndex > name.StartIndex) {
4245
var applicableSpan = context.GetApplicableSpanFromLastToken(import);
43-
return new CompletionResult(Enumerable.Repeat(CompletionItemSource.AsKeyword, 1), applicableSpan);
46+
return new CompletionResult(new []{ CompletionItemSource.AsKeyword }, applicableSpan);
47+
}
48+
49+
if (name.Names.Count == 0 || name.Names[0].EndIndex >= context.Position) {
50+
return new CompletionResult(GetAllImportableModules(context));
4451
}
4552

46-
return new CompletionResult(GetImportsFromModuleName(name.Names, context));
53+
var document = context.Analysis.Document;
54+
var mres = document.Interpreter.ModuleResolution;
55+
var names = name.Names.TakeWhile(n => n.EndIndex < context.Position).Select(n => n.Name);
56+
var importSearchResult = mres.CurrentPathResolver.GetImportsFromAbsoluteName(document.FilePath, names, import.ForceAbsolute);
57+
return GetResultFromImportSearch(importSearchResult, context, false);
4758
}
4859
}
4960
return null;
@@ -71,14 +82,15 @@ public static CompletionResult GetCompletionsInFromImport(FromImportStatement fr
7182

7283
if (context.Position >= name.StartIndex) {
7384
var applicableSpan = name.GetSpan(context.Ast);
74-
return new CompletionResult(GetModuleMembers(fromImport.Root.Names, context), applicableSpan);
85+
var importSearchResult = mres.CurrentPathResolver.FindImports(document.FilePath, fromImport);
86+
return GetResultFromImportSearch(importSearchResult, context, false, applicableSpan);
7587
}
7688
}
7789
}
7890

7991
if (fromImport.ImportIndex > fromImport.StartIndex && context.Position > fromImport.ImportIndex + 6) {
8092
var importSearchResult = mres.CurrentPathResolver.FindImports(document.FilePath, fromImport);
81-
var result = GetResultFromSearch(importSearchResult, context);
93+
var result = GetResultFromImportSearch(importSearchResult, context, true);
8294
if (result != CompletionResult.Empty) {
8395
return result;
8496
}
@@ -106,37 +118,30 @@ public static CompletionResult GetCompletionsInFromImport(FromImportStatement fr
106118
return new CompletionResult(Enumerable.Repeat(CompletionItemSource.ImportKeyword, 1), applicableSpan);
107119
}
108120

109-
if (context.Position >= fromImport.Root.StartIndex) {
110-
return new CompletionResult(GetImportsFromModuleName(fromImport.Root.Names, context));
121+
if (context.Position > fromImport.Root.StartIndex && fromImport.Root is RelativeModuleName relativeName) {
122+
var rootNames = relativeName.Names.Select(n => n.Name);
123+
var importSearchResult = mres.CurrentPathResolver.GetImportsFromRelativePath(document.FilePath, relativeName.DotCount, rootNames);
124+
return GetResultFromImportSearch(importSearchResult, context, false);
125+
}
126+
127+
if (fromImport.Root.Names.Count > 1 && context.Position > fromImport.Root.Names[0].EndIndex) {
128+
var rootNames = fromImport.Root.Names.TakeWhile(n => n.EndIndex < context.Position).Select(n => n.Name);
129+
var importSearchResult = mres.CurrentPathResolver.GetImportsFromAbsoluteName(document.FilePath, rootNames, fromImport.ForceAbsolute);
130+
return GetResultFromImportSearch(importSearchResult, context, false);
111131
}
112132

113133
return context.Position > fromImport.KeywordEndIndex
114134
? new CompletionResult(GetAllImportableModules(context))
115135
: null;
116136
}
117137

118-
private static IEnumerable<CompletionItem> GetImportsFromModuleName(IEnumerable<NameExpression> nameExpressions, CompletionContext context) {
119-
var names = nameExpressions.TakeWhile(n => n.StartIndex <= context.Position).Select(n => n.Name).ToArray();
120-
return names.Length <= 1 ? GetAllImportableModules(context) : GetChildModules(names, context);
121-
}
122-
123-
private static IEnumerable<CompletionItem> GetModuleMembers(IEnumerable<NameExpression> nameExpressions, CompletionContext context) {
124-
var fullName = string.Join(".", nameExpressions.Select(n => n.Name));
125-
var mres = context.Analysis.Document.Interpreter.ModuleResolution;
126-
127-
var module = mres.GetImportedModule(fullName);
128-
return module != null
129-
? module.GetMemberNames().Select(n => context.ItemSource.CreateCompletionItem(n, module.GetMember(n)))
130-
: Enumerable.Empty<CompletionItem>();
131-
}
132-
133138
private static IEnumerable<CompletionItem> GetAllImportableModules(CompletionContext context) {
134139
var mres = context.Analysis.Document.Interpreter.ModuleResolution;
135140
var modules = mres.CurrentPathResolver.GetAllModuleNames().Distinct();
136141
return modules.Select(n => CompletionItemSource.CreateCompletionItem(n, CompletionItemKind.Module));
137142
}
138143

139-
private static CompletionResult GetResultFromSearch(IImportSearchResult importSearchResult, CompletionContext context) {
144+
private static CompletionResult GetResultFromImportSearch(IImportSearchResult importSearchResult, CompletionContext context, bool prependStar, SourceSpan? applicableSpan = null) {
140145
var document = context.Analysis.Document;
141146
var mres = document.Interpreter.ModuleResolution;
142147

@@ -155,15 +160,33 @@ private static CompletionResult GetResultFromSearch(IImportSearchResult importSe
155160
return CompletionResult.Empty;
156161
}
157162

158-
var moduleNames = (importSearchResult as IImportChildrenSource)?.GetChildrenNames() ?? ImmutableArray<string>.Empty;
159-
if (module == null && moduleNames.Count == 0) {
160-
return CompletionResult.Empty;
163+
var completions = new List<CompletionItem>();
164+
if (prependStar) {
165+
completions.Add(CompletionItemSource.Star);
166+
}
167+
168+
if (module != null) {
169+
completions.AddRange(module.GetMemberNames().Select(n => context.ItemSource.CreateCompletionItem(n, module?.GetMember(n))));
170+
}
171+
172+
if (importSearchResult is IImportChildrenSource children) {
173+
foreach (var childName in children.GetChildrenNames()) {
174+
if (!children.TryGetChildImport(childName, out var imports)) {
175+
continue;
176+
}
177+
178+
switch (imports) {
179+
case ImplicitPackageImport packageImport:
180+
completions.Add(CompletionItemSource.CreateCompletionItem(packageImport.Name, CompletionItemKind.Module));
181+
break;
182+
case ModuleImport moduleImport when !moduleImport.ModulePath.PathEquals(document.FilePath):
183+
completions.Add(CompletionItemSource.CreateCompletionItem(moduleImport.Name, CompletionItemKind.Module));
184+
break;
185+
}
186+
}
161187
}
162188

163-
var memberNames = module?.GetMemberNames() ?? ImmutableArray<string>.Empty;
164-
var members = memberNames.Select(n => context.ItemSource.CreateCompletionItem(n, module?.GetMember(n)));
165-
var modules = moduleNames.Select(n => CompletionItemSource.CreateCompletionItem(n, CompletionItemKind.Module));
166-
return new CompletionResult(members.Concat(modules).Prepend(CompletionItemSource.Star));
189+
return new CompletionResult(completions, applicableSpan);
167190
}
168191

169192
private static IReadOnlyList<CompletionItem> GetChildModules(string[] names, CompletionContext context) {

src/LanguageServer/Test/CompletionTests.cs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
using System.Threading.Tasks;
1919
using FluentAssertions;
2020
using Microsoft.Python.Analysis;
21+
using Microsoft.Python.Analysis.Documents;
2122
using Microsoft.Python.Analysis.Modules;
2223
using Microsoft.Python.Analysis.Types;
2324
using Microsoft.Python.Core;
@@ -706,6 +707,38 @@ public async Task InWithStatement() {
706707
result.Should().HaveNoCompletion();
707708
}
708709

710+
711+
[TestMethod, Priority(0)]
712+
public async Task ImportInPackage() {
713+
var module1Path = TestData.GetTestSpecificUri("package", "module1.py");
714+
var module2Path = TestData.GetTestSpecificUri("package", "module2.py");
715+
var module3Path = TestData.GetTestSpecificUri("package", "sub_package", "module3.py");
716+
717+
var root = TestData.GetTestSpecificRootUri().AbsolutePath;
718+
await CreateServicesAsync(root, PythonVersions.LatestAvailable3X);
719+
var rdt = Services.GetService<IRunningDocumentTable>();
720+
721+
var module1 = rdt.OpenDocument(module1Path, "import package.");
722+
var module2 = rdt.OpenDocument(module2Path, "import package.sub_package.");
723+
var module3 = rdt.OpenDocument(module3Path, "import package.");
724+
725+
var analysis1 = await module1.GetAnalysisAsync(-1);
726+
var analysis2 = await module2.GetAnalysisAsync(-1);
727+
var analysis3 = await module3.GetAnalysisAsync(-1);
728+
729+
var cs = new CompletionSource(new PlainTextDocumentationSource(), ServerSettings.completion);
730+
731+
var result = cs.GetCompletions(analysis1, new SourceLocation(1, 16));
732+
result.Should().OnlyHaveLabels("module2", "sub_package");
733+
result = cs.GetCompletions(analysis2, new SourceLocation(1, 16));
734+
result.Should().OnlyHaveLabels("module1", "sub_package");
735+
result = cs.GetCompletions(analysis2, new SourceLocation(1, 28));
736+
result.Should().OnlyHaveLabels("module3");
737+
result = cs.GetCompletions(analysis3, new SourceLocation(1, 16));
738+
result.Should().OnlyHaveLabels("module1", "module2", "sub_package");
739+
}
740+
741+
709742
[TestMethod, Priority(0)]
710743
public async Task InImport() {
711744
var code = @"
@@ -860,6 +893,86 @@ import os
860893
result.Should().HaveLabels("split", @"getsize", @"islink", @"abspath");
861894
}
862895

896+
[TestMethod, Priority(0)]
897+
public async Task FromDotInRoot() {
898+
const string code = "from .";
899+
var analysis = await GetAnalysisAsync(code, PythonVersions.LatestAvailable3X);
900+
var cs = new CompletionSource(new PlainTextDocumentationSource(), ServerSettings.completion);
901+
902+
var result = cs.GetCompletions(analysis, new SourceLocation(1, 7));
903+
result.Should().HaveNoCompletion();
904+
}
905+
906+
[TestMethod, Priority(0)]
907+
public async Task FromDotInExplicitPackage() {
908+
var initPyPath = TestData.GetTestSpecificUri("package", "__init__.py");
909+
var module1Path = TestData.GetTestSpecificUri("package", "module1.py");
910+
var module2Path = TestData.GetTestSpecificUri("package", "module2.py");
911+
var module3Path = TestData.GetTestSpecificUri("package", "sub_package", "module3.py");
912+
913+
var root = TestData.GetTestSpecificRootUri().AbsolutePath;
914+
await CreateServicesAsync(root, PythonVersions.LatestAvailable3X);
915+
var rdt = Services.GetService<IRunningDocumentTable>();
916+
917+
rdt.OpenDocument(initPyPath, "answer = 42");
918+
var module = rdt.OpenDocument(module1Path, "from .");
919+
rdt.OpenDocument(module2Path, string.Empty);
920+
rdt.OpenDocument(module3Path, string.Empty);
921+
922+
var analysis = await module.GetAnalysisAsync(-1);
923+
var cs = new CompletionSource(new PlainTextDocumentationSource(), ServerSettings.completion);
924+
925+
var result = cs.GetCompletions(analysis, new SourceLocation(1, 7));
926+
result.Should().OnlyHaveLabels("module2", "sub_package", "answer");
927+
}
928+
929+
[TestMethod, Priority(0)]
930+
public async Task FromPartialName() {
931+
var initPyPath = TestData.GetTestSpecificUri("package", "__init__.py");
932+
var module1Path = TestData.GetTestSpecificUri("package", "module1.py");
933+
var module2Path = TestData.GetTestSpecificUri("package", "sub_package", "module2.py");
934+
935+
var root = TestData.GetTestSpecificRootUri().AbsolutePath;
936+
await CreateServicesAsync(root, PythonVersions.LatestAvailable3X);
937+
var rdt = Services.GetService<IRunningDocumentTable>();
938+
939+
var module = rdt.OpenDocument(initPyPath, "answer = 42");
940+
var module1 = rdt.OpenDocument(module1Path, "from pa");
941+
var module2 = rdt.OpenDocument(module2Path, "from package.su");
942+
module1.Interpreter.ModuleResolution.GetOrLoadModule("package");
943+
944+
await module.GetAnalysisAsync(-1);
945+
var analysis1 = await module1.GetAnalysisAsync(-1);
946+
var analysis2 = await module2.GetAnalysisAsync(-1);
947+
var cs = new CompletionSource(new PlainTextDocumentationSource(), ServerSettings.completion);
948+
949+
var result = cs.GetCompletions(analysis1, new SourceLocation(1, 8));
950+
result.Should().HaveLabels("package").And.NotContainLabels("module2", "sub_package", "answer");
951+
result = cs.GetCompletions(analysis2, new SourceLocation(1, 16));
952+
result.Should().OnlyHaveLabels("module1", "sub_package", "answer");
953+
}
954+
955+
[TestMethod, Priority(0)]
956+
public async Task FromDotInImplicitPackage() {
957+
var module1 = TestData.GetTestSpecificUri("package", "module1.py");
958+
var module2 = TestData.GetTestSpecificUri("package", "module2.py");
959+
var module3 = TestData.GetTestSpecificUri("package", "sub_package", "module3.py");
960+
961+
var root = TestData.GetTestSpecificRootUri().AbsolutePath;
962+
await CreateServicesAsync(root, PythonVersions.LatestAvailable3X);
963+
var rdt = Services.GetService<IRunningDocumentTable>();
964+
965+
var module = rdt.OpenDocument(module1, "from .");
966+
rdt.OpenDocument(module2, string.Empty);
967+
rdt.OpenDocument(module3, string.Empty);
968+
969+
var analysis = await module.GetAnalysisAsync(-1);
970+
var cs = new CompletionSource(new PlainTextDocumentationSource(), ServerSettings.completion);
971+
972+
var result = cs.GetCompletions(analysis, new SourceLocation(1, 7));
973+
result.Should().OnlyHaveLabels("module2", "sub_package");
974+
}
975+
863976
[DataRow(false)]
864977
[DataRow(true)]
865978
[DataTestMethod, Priority(0)]

0 commit comments

Comments
 (0)