Skip to content

Commit 9a9b269

Browse files
committed
Fix microsoft#281: Support "go to definition" for namespace packages
Fix microsoft#466: Fix "go to definition" and resolving imports The fix is to put user search paths in front of workspace directory so that modules inside extra paths can be used as roots for packages
1 parent 64eae15 commit 9a9b269

File tree

4 files changed

+62
-7
lines changed

4 files changed

+62
-7
lines changed

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

+12-5
Original file line numberDiff line numberDiff line change
@@ -359,11 +359,11 @@ private void CreateRootsWithDefault(string rootDirectory, string[] userSearchPat
359359
.ToArray();
360360

361361
var filteredInterpreterSearchPaths = interpreterSearchPaths.Select(FixPath)
362-
.Except(filteredUserSearchPaths.Prepend(rootDirectory))
362+
.Except(filteredUserSearchPaths.Append(rootDirectory))
363363
.ToArray();
364364

365365
userRootsCount = filteredUserSearchPaths.Length + 1;
366-
nodes = AddRootsFromSearchPaths(ImmutableArray<Node>.Empty.Add(GetOrCreateRoot(rootDirectory)), filteredUserSearchPaths, filteredInterpreterSearchPaths);
366+
nodes = AddRootsFromSearchPaths(rootDirectory, filteredUserSearchPaths, filteredInterpreterSearchPaths);
367367

368368
string FixPath(string p) => Path.IsPathRooted(p) ? PathUtils.NormalizePath(p) : PathUtils.NormalizePath(Path.Combine(rootDirectory, p));
369369
}
@@ -381,11 +381,18 @@ private void CreateRootsWithoutDefault(string[] userSearchPaths, string[] interp
381381
.ToArray();
382382

383383
userRootsCount = filteredUserSearchPaths.Length;
384-
nodes = AddRootsFromSearchPaths(ImmutableArray<Node>.Empty, filteredUserSearchPaths, filteredInterpreterSearchPaths);
384+
nodes = AddRootsFromSearchPaths(filteredUserSearchPaths, filteredInterpreterSearchPaths);
385385
}
386386

387-
private ImmutableArray<Node> AddRootsFromSearchPaths(ImmutableArray<Node> roots, string[] userSearchPaths, string[] interpreterSearchPaths) {
388-
return roots
387+
private ImmutableArray<Node> AddRootsFromSearchPaths(string rootDirectory, string[] userSearchPaths, string[] interpreterSearchPaths) {
388+
return ImmutableArray<Node>.Empty
389+
.AddRange(userSearchPaths.Select(GetOrCreateRoot).ToArray())
390+
.Add(GetOrCreateRoot(rootDirectory))
391+
.AddRange(interpreterSearchPaths.Select(GetOrCreateRoot).ToArray());
392+
}
393+
394+
private ImmutableArray<Node> AddRootsFromSearchPaths(string[] userSearchPaths, string[] interpreterSearchPaths) {
395+
return ImmutableArray<Node>.Empty
389396
.AddRange(userSearchPaths.Select(GetOrCreateRoot).ToArray())
390397
.AddRange(interpreterSearchPaths.Select(GetOrCreateRoot).ToArray());
391398
}

src/Analysis/Engine/Impl/PythonAnalyzer.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,8 @@ private async Task LoadKnownTypesAsync(CancellationToken token) {
129129
}
130130

131131
private void ReloadModulePaths(in IEnumerable<string> rootPaths) {
132-
foreach (var modulePath in rootPaths.Where(Directory.Exists).SelectMany(p => ModulePath.GetModulesInPath(p))) {
133-
_pathResolver.TryAddModulePath(modulePath.SourceFile, out _);
132+
foreach (var modulePath in rootPaths.Where(Directory.Exists).SelectMany(p => PathUtils.EnumerateFiles(p))) {
133+
_pathResolver.TryAddModulePath(modulePath, out _);
134134
}
135135
}
136136

src/Analysis/Engine/Test/ImportTests.cs

+40
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,46 @@ import package.sub_package.module2
9898
completionModule2.Should().HaveLabels("Y").And.NotContainLabels("X");
9999
}
100100

101+
[ServerTestMethod(LatestAvailable3X = true, TestSpecificRootUri = true), Priority(0)]
102+
public async Task Completions_ImportResolution_UserSearchPathsInsideRoot(Server server) {
103+
var folder1 = TestData.GetTestSpecificPath("folder1");
104+
var folder2 = TestData.GetTestSpecificPath("folder2");
105+
var packageInFolder1 = Path.Combine(folder1, "package");
106+
var packageInFolder2 = Path.Combine(folder2, "package");
107+
var module1Path = Path.Combine(packageInFolder1, "module1.py");
108+
var module2Path = Path.Combine(packageInFolder2, "module2.py");
109+
var module1Content = @"class A():
110+
@staticmethod
111+
def method1():
112+
pass";
113+
var module2Content = @"class B():
114+
@staticmethod
115+
def method2():
116+
pass";
117+
var mainContent = @"from package import module1 as mod1, module2 as mod2
118+
mod1.
119+
mod2.
120+
mod1.A.
121+
mod2.B.";
122+
123+
server.Analyzer.SetSearchPaths(new[] { folder1, folder2 });
124+
125+
await server.OpenDocumentAndGetUriAsync(module1Path, module1Content);
126+
await server.OpenDocumentAndGetUriAsync(module2Path, module2Content);
127+
var uri = await server.OpenDocumentAndGetUriAsync("main.py", mainContent);
128+
129+
await server.WaitForCompleteAnalysisAsync(CancellationToken.None);
130+
131+
var completionMod1 = await server.SendCompletion(uri, 1, 5);
132+
var completionMod2 = await server.SendCompletion(uri, 2, 5);
133+
var completionA = await server.SendCompletion(uri, 3, 7);
134+
var completionB = await server.SendCompletion(uri, 4, 7);
135+
completionMod1.Should().HaveLabels("A").And.NotContainLabels("B");
136+
completionMod2.Should().HaveLabels("B").And.NotContainLabels("A");
137+
completionA.Should().HaveLabels("method1");
138+
completionB.Should().HaveLabels("method2");
139+
}
140+
101141
[Ignore("https://github.com/Microsoft/python-language-server/issues/443")]
102142
[ServerTestMethod(LatestAvailable3X = true, TestSpecificRootUri = true), Priority(0)]
103143
public async Task Completions_ImportResolution_OneSearchPathInsideAnother(Server server) {

src/Analysis/Engine/Test/ServerExtensions.cs

+8
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,14 @@ await server.DidOpenTextDocument(new DidOpenTextDocumentParams {
164164
}, GetCancellationToken());
165165
}
166166

167+
public static async Task<IModuleAnalysis> OpenDocumentAndGetAnalysisAsync(this Server server, string relativePath, string content, int failAfter = 30000, string languageId = null) {
168+
var cancellationToken = GetCancellationToken(failAfter);
169+
var uri = TestData.GetTestSpecificUri(relativePath);
170+
await server.SendDidOpenTextDocument(uri, content, languageId);
171+
cancellationToken.ThrowIfCancellationRequested();
172+
return await server.GetAnalysisAsync(uri, cancellationToken);
173+
}
174+
167175
public static async Task<IModuleAnalysis> OpenDefaultDocumentAndGetAnalysisAsync(this Server server, string content, int failAfter = 30000, string languageId = null) {
168176
var cancellationToken = GetCancellationToken(failAfter);
169177
await server.SendDidOpenTextDocument(TestData.GetDefaultModuleUri(), content, languageId);

0 commit comments

Comments
 (0)