diff --git a/src/Analysis/Engine/Test/AnalysisTest.cs b/src/Analysis/Engine/Test/AnalysisTest.cs
index 1c3bf74ac..a45a781f8 100644
--- a/src/Analysis/Engine/Test/AnalysisTest.cs
+++ b/src/Analysis/Engine/Test/AnalysisTest.cs
@@ -38,7 +38,7 @@
namespace AnalysisTests {
[TestClass]
- public partial class AnalysisTest {
+ public class AnalysisTest {
public TestContext TestContext { get; set; }
[TestInitialize]
@@ -864,102 +864,6 @@ import mod2
}
*/
- [TestMethod, Priority(0)]
- public async Task PrivateMembers() {
- using (var server = await CreateServerAsync(PythonVersions.LatestAvailable2X)) {
- var uri = TestData.GetTempPathUri("test-module.py");
-
- string code = @"
-class C:
- def __init__(self):
- self._C__X = 'abc' # Completions here should only ever show __X
- self.__X = 42
-
-class D(C):
- def __init__(self):
- print(self.__X) # self. here shouldn't have __X or _C__X (could be controlled by Text Editor->Python->general->Hide advanced members to show _C__X)
-";
-
- await server.SendDidOpenTextDocument(uri, code);
- await server.GetAnalysisAsync(uri);
-
- var completions = await server.SendCompletion(uri, 4, 13);
- completions.Should().OnlyHaveLabels("__X", "__init__", "__doc__", "__class__");
-
- completions = await server.SendCompletion(uri, 8, 19);
- completions.Should().OnlyHaveLabels("_C__X", "__init__", "__doc__", "__class__");
-
- code = @"
-class C(object):
- def __init__(self):
- self.f(_C__A = 42) # sig help should be _C__A
-
- def f(self, __A):
- pass
-
-
-class D(C):
- def __init__(self):
- self.f(_C__A=42) # sig help should be _C__A
-";
-
- await server.SendDidChangeTextDocumentAsync(uri, code);
- await server.GetAnalysisAsync(uri);
-
- var signatures = await server.SendSignatureHelp(uri, 3, 15);
- signatures.Should().HaveSingleSignature()
- .Which.Should().OnlyHaveParameterLabels("_C__A");
-
- signatures = await server.SendSignatureHelp(uri, 11, 15);
- signatures.Should().HaveSingleSignature()
- .Which.Should().OnlyHaveParameterLabels("_C__A");
-
- code = @"
-class C(object):
- def __init__(self):
- self.__f(_C__A = 42) # member should be __f
-
- def __f(self, __A):
- pass
-
-
-class D(C):
- def __init__(self):
- self._C__f(_C__A=42) # member should be _C__f
-
-";
-
- await server.SendDidChangeTextDocumentAsync(uri, code);
- await server.GetAnalysisAsync(uri);
-
- completions = await server.SendCompletion(uri, 3, 13);
- completions.Should().HaveLabels("__f", "__init__");
-
- completions = await server.SendCompletion(uri, 11, 13);
- completions.Should().HaveLabels("_C__f", "__init__");
-
- code = @"
-class C(object):
- __FOB = 42
-
- def f(self):
- abc = C.__FOB # Completion should work here
-
-
-xyz = C._C__FOB # Advanced members completion should work here
-";
-
- await server.SendDidChangeTextDocumentAsync(uri, code);
- await server.GetAnalysisAsync(uri);
-
- completions = await server.SendCompletion(uri, 5, 16);
- completions.Should().HaveLabels("__FOB", "f");
-
- completions = await server.SendCompletion(uri, 8, 8);
- completions.Should().HaveLabels("_C__FOB", "f");
- }
- }
-
[TestMethod, Priority(0)]
public async Task BaseInstanceVariable() {
var code = @"
@@ -3975,28 +3879,6 @@ def f(x = x):
}
}
- [TestMethod, Priority(0)]
- public async Task RecursiveClass() {
- var code = @"
-cls = object
-
-class cls(cls):
- abc = 42
-
-a = cls().abc
-b = cls.abc
-";
- using (var server = await CreateServerAsync()) {
- var analysis = await server.OpenDefaultDocumentAndGetAnalysisAsync(code);
- var completion = await server.SendCompletion(TestData.GetDefaultModuleUri(), 8, 0);
-
- analysis.Should().HaveVariable("a").OfType(BuiltinTypeId.Int)
- .And.HaveVariable("b").OfType(BuiltinTypeId.Int);
-
- completion.Should().HaveLabels("cls", "object");
- }
- }
-
[TestMethod, Priority(0)]
public async Task BadMethod() {
var code = @"
@@ -4134,43 +4016,6 @@ public async Task KeywordSplat(string functionDeclaration, int signatureLine, st
}
}
- [TestMethod, Priority(0)]
- public async Task ForwardRef() {
- var code = @"
-
-class D(object):
- def oar(self, x):
- abc = C()
- abc.fob(2)
- a = abc.fob(2.0)
- a.oar(('a', 'b', 'c', 'd'))
-
-class C(object):
- def fob(self, x):
- D().oar('abc')
- D().oar(['a', 'b', 'c'])
- return D()
- def baz(self): pass
-";
- using (var server = await CreateServerAsync(PythonVersions.LatestAvailable2X)) {
- var analysis = await server.OpenDefaultDocumentAndGetAnalysisAsync(code);
- var completionInD = await server.SendCompletion(TestData.GetDefaultModuleUri(), 3, 4);
- var completionInOar = await server.SendCompletion(TestData.GetDefaultModuleUri(), 5, 8);
- var completionForAbc = await server.SendCompletion(TestData.GetDefaultModuleUri(), 5, 12);
-
- completionInD.Should().HaveLabels("C", "D", "oar")
- .And.NotContainLabels("a", "abc", "self", "x", "fob", "baz");
-
- completionInOar.Should().HaveLabels("C", "D", "a", "oar", "abc", "self", "x")
- .And.NotContainLabels("fob", "baz");
-
- completionForAbc.Should().HaveLabels("baz", "fob");
-
- analysis.Should().HaveClass("D").WithFunction("oar")
- .Which.Should().HaveParameter("x").OfTypes(BuiltinTypeId.List, BuiltinTypeId.Str, BuiltinTypeId.Tuple);
- }
- }
-
[TestMethod, Priority(0)]
public async Task Builtins() {
@@ -4277,28 +4122,6 @@ def g(a, b):
}
}
- [TestMethod, Priority(0)]
- public async Task SimpleGlobals() {
- var code = @"
-class x(object):
- def abc(self):
- pass
-
-a = x()
-x.abc()
-";
- using (var server = await CreateServerAsync()) {
- var objectMemberNames = server.GetBuiltinTypeMemberNames(BuiltinTypeId.Object);
-
- var uri = await server.OpenDefaultDocumentAndGetUriAsync(code);
- var completion = await server.SendCompletion(uri, 6, 0);
- var completionX = await server.SendCompletion(uri, 6, 2);
-
- completion.Should().HaveLabels("a", "x").And.NotContainLabels("abc", "self");
- completionX.Should().HaveLabels(objectMemberNames).And.HaveLabels("abc");
- }
- }
-
[TestMethod, Priority(0)]
public async Task FuncCallInIf() {
var code = @"
@@ -4403,67 +4226,6 @@ def f(a, b, c=0):
}
}
- ///
- /// http://pytools.codeplex.com/workitem/799
- ///
- [TestMethod, Priority(0)]
- public async Task OverrideCompletions2X() {
- var code = @"
-class oar(list):
- def
- pass
-";
- using (var server = await CreateServerAsync(PythonVersions.LatestAvailable2X)) {
- var uri = await server.OpenDefaultDocumentAndGetUriAsync(code);
- var completions = await server.SendCompletion(uri, 2, 8);
-
- completions.Should().HaveItem("append")
- .Which.Should().HaveInsertText("append(self, value):\r\n return super(oar, self).append(value)");
- }
- }
-
- [DataRow(PythonLanguageVersion.V36, "value")]
- [DataRow(PythonLanguageVersion.V37, "object")]
- [DataTestMethod, Priority(0)]
- public async Task OverrideCompletions3X(PythonLanguageVersion version, string parameterName) {
- var code = @"
-class oar(list):
- def
- pass
-";
- using (var server = await CreateServerAsync(PythonVersions.GetRequiredCPythonConfiguration(version))) {
- var uri = await server.OpenDefaultDocumentAndGetUriAsync(code);
- var completions = await server.SendCompletion(uri, 2, 8);
-
- completions.Should().HaveItem("append")
- .Which.Should().HaveInsertText($"append(self, {parameterName}):\r\n return super().append({parameterName})");
- }
- }
-
- [TestMethod, Priority(0)]
- public async Task OverrideCompletionsNested() {
- // Ensure that nested classes are correctly resolved.
- var code = @"
-class oar(int):
- class fob(dict):
- def
- pass
- def
- pass
-";
-
- using (var server = await CreateServerAsync(PythonVersions.LatestAvailable2X)) {
- var uri = await server.OpenDefaultDocumentAndGetUriAsync(code);
- var completionsOar = await server.SendCompletion(uri, 5, 8);
- var completionsFob = await server.SendCompletion(uri, 3, 12);
-
- completionsOar.Should().NotContainLabels("keys", "items")
- .And.HaveItem("bit_length");
- completionsFob.Should().NotContainLabels("bit_length")
- .And.HaveLabels("keys", "items");
- }
- }
-
///
/// https://github.com/Microsoft/PTVS/issues/995
///
@@ -6618,32 +6380,6 @@ def with_params_default_starargs(*args, **kwargs):
.And.HaveVariable("rf").WithDescription("method return_func of module.return_func_class objects...")
.WithDocumentation("some help");
}
-
-
- }
-
- [TestMethod, Priority(0)]
- public async Task CompletionDocumentation() {
- var text = @"
-import sys
-z = 43
-
-class fob(object):
- @property
- def f(self): pass
-
- def g(self): pass
-
-d = fob()
-";
- using (var server = await CreateServerAsync()) {
- var uri = await server.OpenDefaultDocumentAndGetUriAsync(text);
- var completionD = await server.SendCompletion(uri, 15, 1);
- completionD.Should().HaveItem("d")
- .Which.Should().HaveDocumentation("fob");
- completionD.Should().HaveItem("z")
- .Which.Should().HaveDocumentation("int");
- }
}
[TestMethod, Priority(0)]
@@ -6848,73 +6584,6 @@ print abc
}
}
- [TestMethod, Priority(0)]
- public async Task TypeAtEndOfMethod() {
- var text = @"
-class Fob(object):
- def oar(self, a):
- pass
-
-
- def fob(self):
- pass
-
-x = Fob()
-x.oar(100)
-";
-
- using (var server = await CreateServerAsync()) {
- var uri = await server.OpenDefaultDocumentAndGetUriAsync(text);
- var completion = await server.SendCompletion(uri, 5, 8);
- completion.Should().HaveItem("a")
- .Which.Should().HaveDocumentation("int");
- }
- }
-
- [TestMethod, Priority(0)]
- public async Task TypeAtEndOfIncompleteMethod() {
- var text = @"
-class Fob(object):
- def oar(self, a):
-
-
-
-
-
-x = Fob()
-x.oar(100)
-";
-
- using (var server = await CreateServerAsync()) {
- var uri = await server.OpenDefaultDocumentAndGetUriAsync(text);
- var completion = await server.SendCompletion(uri, 5, 8);
- completion.Should().HaveItem("a")
- .Which.Should().HaveDocumentation("int");
- }
- }
-
- [TestMethod, Priority(0)]
- public async Task TypeIntersectionUserDefinedTypes() {
- var text = @"
-class C1(object):
- def fob(self): pass
-
-class C2(object):
- def oar(self): pass
-
-c = C1()
-c.fob()
-c = C2()
-c.
-";
-
- using (var server = await CreateServerAsync()) {
- var uri = await server.OpenDefaultDocumentAndGetUriAsync(text);
- var completion = await server.SendCompletion(uri, 10, 2);
- completion.Should().NotContainLabels("fob", "oar");
- }
- }
-
[TestMethod, Priority(0)]
public async Task UpdateMethodMultiFiles() {
var text1 = @"
diff --git a/src/Analysis/Engine/Test/CompletionTest.cs b/src/Analysis/Engine/Test/CompletionTest.cs
new file mode 100644
index 000000000..9009a7c26
--- /dev/null
+++ b/src/Analysis/Engine/Test/CompletionTest.cs
@@ -0,0 +1,413 @@
+// Python Tools for Visual Studio
+// Copyright(c) Microsoft Corporation
+// All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the License); you may not use
+// this file except in compliance with the License. You may obtain a copy of the
+// License at http://www.apache.org/licenses/LICENSE-2.0
+//
+// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
+// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
+// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+// MERCHANTABILITY OR NON-INFRINGEMENT.
+//
+// See the Apache Version 2.0 License for specific language governing
+// permissions and limitations under the License.
+
+using System.Threading.Tasks;
+using Microsoft.Python.LanguageServer;
+using Microsoft.Python.LanguageServer.Implementation;
+using Microsoft.PythonTools.Analysis;
+using Microsoft.PythonTools.Analysis.FluentAssertions;
+using Microsoft.PythonTools.Analysis.Infrastructure;
+using Microsoft.PythonTools.Interpreter;
+using Microsoft.PythonTools.Parsing;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using TestUtilities;
+
+namespace AnalysisTests {
+ [TestClass]
+ public class CompletionTest {
+ public TestContext TestContext { get; set; }
+
+ [TestInitialize]
+ public void TestInitialize() {
+ TestEnvironmentImpl.TestInitialize($"{TestContext.FullyQualifiedTestClassName}.{TestContext.TestName}");
+ }
+
+ [TestCleanup]
+ public void TestCleanup() {
+ TestEnvironmentImpl.TestCleanup();
+ }
+
+ [ServerTestMethod, Priority(0)]
+ public async Task CompletionInWithStatementDerivedClass(Server server) {
+ var uri = await server.OpenDefaultDocumentAndGetUriAsync("with open(x) as fs:\n fs. ");
+ await server.GetAnalysisAsync(uri);
+ var completions = await server.SendCompletion(uri, 1, 5);
+
+ completions.Should().HaveLabels("read", "write");
+ await server.UnloadFileAsync(uri);
+ }
+
+ [ServerTestMethod(LatestAvailable2X = true), Priority(0)]
+ public async Task PrivateMembers(Server server) {
+ var uri = TestData.GetTempPathUri("test-module.py");
+
+ string code = @"
+class C:
+ def __init__(self):
+ self._C__X = 'abc' # Completions here should only ever show __X
+ self.__X = 42
+
+class D(C):
+ def __init__(self):
+ print(self.__X) # self. here shouldn't have __X or _C__X (could be controlled by Text Editor->Python->general->Hide advanced members to show _C__X)
+";
+
+ await server.SendDidOpenTextDocument(uri, code);
+ await server.GetAnalysisAsync(uri);
+
+ var completions = await server.SendCompletion(uri, 4, 13);
+ completions.Should().OnlyHaveLabels("__X", "__init__", "__doc__", "__class__");
+
+ completions = await server.SendCompletion(uri, 8, 19);
+ completions.Should().OnlyHaveLabels("_C__X", "__init__", "__doc__", "__class__");
+
+ code = @"
+class C(object):
+ def __init__(self):
+ self.f(_C__A = 42) # sig help should be _C__A
+
+ def f(self, __A):
+ pass
+
+
+class D(C):
+ def __init__(self):
+ self.f(_C__A=42) # sig help should be _C__A
+";
+
+ await server.SendDidChangeTextDocumentAsync(uri, code);
+ await server.GetAnalysisAsync(uri);
+
+ var signatures = await server.SendSignatureHelp(uri, 3, 15);
+ signatures.Should().HaveSingleSignature()
+ .Which.Should().OnlyHaveParameterLabels("_C__A");
+
+ signatures = await server.SendSignatureHelp(uri, 11, 15);
+ signatures.Should().HaveSingleSignature()
+ .Which.Should().OnlyHaveParameterLabels("_C__A");
+
+ code = @"
+class C(object):
+ def __init__(self):
+ self.__f(_C__A = 42) # member should be __f
+
+ def __f(self, __A):
+ pass
+
+
+class D(C):
+ def __init__(self):
+ self._C__f(_C__A=42) # member should be _C__f
+
+";
+
+ await server.SendDidChangeTextDocumentAsync(uri, code);
+ await server.GetAnalysisAsync(uri);
+
+ completions = await server.SendCompletion(uri, 3, 13);
+ completions.Should().HaveLabels("__f", "__init__");
+
+ completions = await server.SendCompletion(uri, 11, 13);
+ completions.Should().HaveLabels("_C__f", "__init__");
+
+ code = @"
+class C(object):
+ __FOB = 42
+
+ def f(self):
+ abc = C.__FOB # Completion should work here
+
+
+xyz = C._C__FOB # Advanced members completion should work here
+";
+
+ await server.SendDidChangeTextDocumentAsync(uri, code);
+ await server.GetAnalysisAsync(uri);
+
+ completions = await server.SendCompletion(uri, 5, 16);
+ completions.Should().HaveLabels("__FOB", "f");
+
+ completions = await server.SendCompletion(uri, 8, 8);
+ completions.Should().HaveLabels("_C__FOB", "f");
+ }
+
+ [ServerTestMethod, Priority(0)]
+ public async Task RecursiveClass(Server server) {
+ var code = @"
+cls = object
+
+class cls(cls):
+ abc = 42
+
+a = cls().abc
+b = cls.abc
+";
+
+ var analysis = await server.OpenDefaultDocumentAndGetAnalysisAsync(code);
+ var completion = await server.SendCompletion(TestData.GetDefaultModuleUri(), 8, 0);
+
+ analysis.Should().HaveVariable("a").OfType(BuiltinTypeId.Int)
+ .And.HaveVariable("b").OfType(BuiltinTypeId.Int);
+
+ completion.Should().HaveLabels("cls", "object");
+ }
+
+ [ServerTestMethod, Priority(0)]
+ public async Task ForwardRef(Server server) {
+ var code = @"
+
+class D(object):
+ def oar(self, x):
+ abc = C()
+ abc.fob(2)
+ a = abc.fob(2.0)
+ a.oar(('a', 'b', 'c', 'd'))
+
+class C(object):
+ def fob(self, x):
+ D().oar('abc')
+ D().oar(['a', 'b', 'c'])
+ return D()
+ def baz(self): pass
+";
+
+ var analysis = await server.OpenDefaultDocumentAndGetAnalysisAsync(code);
+ var completionInD = await server.SendCompletion(TestData.GetDefaultModuleUri(), 3, 4);
+ var completionInOar = await server.SendCompletion(TestData.GetDefaultModuleUri(), 5, 8);
+ var completionForAbc = await server.SendCompletion(TestData.GetDefaultModuleUri(), 5, 12);
+
+ completionInD.Should().HaveLabels("C", "D", "oar")
+ .And.NotContainLabels("a", "abc", "self", "x", "fob", "baz");
+
+ completionInOar.Should().HaveLabels("C", "D", "a", "oar", "abc", "self", "x")
+ .And.NotContainLabels("fob", "baz");
+
+ completionForAbc.Should().HaveLabels("baz", "fob");
+
+ analysis.Should().HaveClass("D").WithFunction("oar")
+ .Which.Should().HaveParameter("x").OfTypes(BuiltinTypeId.List, BuiltinTypeId.Str, BuiltinTypeId.Tuple);
+ }
+
+ [ServerTestMethod, Priority(0)]
+ public async Task SimpleGlobals(Server server) {
+ var code = @"
+class x(object):
+ def abc(self):
+ pass
+
+a = x()
+x.abc()
+";
+ var objectMemberNames = server.GetBuiltinTypeMemberNames(BuiltinTypeId.Object);
+
+ var uri = await server.OpenDefaultDocumentAndGetUriAsync(code);
+ var completion = await server.SendCompletion(uri, 6, 0);
+ var completionX = await server.SendCompletion(uri, 6, 2);
+
+ completion.Should().HaveLabels("a", "x").And.NotContainLabels("abc", "self");
+ completionX.Should().HaveLabels(objectMemberNames).And.HaveLabels("abc");
+ }
+
+ ///
+ /// http://pytools.codeplex.com/workitem/799
+ ///
+ [ServerTestMethod(LatestAvailable2X = true), Priority(0)]
+ public async Task OverrideCompletions2X(Server server) {
+ var code = @"
+class oar(list):
+ def
+ pass
+";
+ var uri = await server.OpenDefaultDocumentAndGetUriAsync(code);
+ var completions = await server.SendCompletion(uri, 2, 8);
+
+ completions.Should().HaveItem("append")
+ .Which.Should().HaveInsertText("append(self, value):\r\n return super(oar, self).append(value)");
+ }
+
+ [DataRow(PythonLanguageVersion.V36, "value")]
+ [DataRow(PythonLanguageVersion.V37, "object")]
+ [ServerTestMethod(VersionArgumentIndex = 1), Priority(0)]
+ public async Task OverrideCompletions3X(Server server, PythonLanguageVersion version, string parameterName) {
+ var code = @"
+class oar(list):
+ def
+ pass
+";
+ var uri = await server.OpenDefaultDocumentAndGetUriAsync(code);
+ var completions = await server.SendCompletion(uri, 2, 8);
+
+ completions.Should().HaveItem("append")
+ .Which.Should().HaveInsertText($"append(self, {parameterName}):\r\n return super().append({parameterName})");
+ }
+
+ [ServerTestMethod(LatestAvailable2X = true), Priority(0)]
+ public async Task OverrideCompletionsNested(Server server) {
+ // Ensure that nested classes are correctly resolved.
+ var code = @"
+class oar(int):
+ class fob(dict):
+ def
+ pass
+ def
+ pass
+";
+
+
+ var uri = await server.OpenDefaultDocumentAndGetUriAsync(code);
+ var completionsOar = await server.SendCompletion(uri, 5, 8);
+ var completionsFob = await server.SendCompletion(uri, 3, 12);
+
+ completionsOar.Should().NotContainLabels("keys", "items")
+ .And.HaveItem("bit_length");
+ completionsFob.Should().NotContainLabels("bit_length")
+ .And.HaveLabels("keys", "items");
+ }
+
+ [ServerTestMethod, Priority(0)]
+ public async Task CompletionDocumentation(Server server) {
+ var text = @"
+import sys
+z = 43
+
+class fob(object):
+ @property
+ def f(self): pass
+
+ def g(self): pass
+
+d = fob()
+";
+ var uri = await server.OpenDefaultDocumentAndGetUriAsync(text);
+ var completionD = await server.SendCompletion(uri, 15, 1);
+ completionD.Should().HaveItem("d")
+ .Which.Should().HaveDocumentation("fob");
+ completionD.Should().HaveItem("z")
+ .Which.Should().HaveDocumentation("int");
+ }
+
+ [ServerTestMethod, Priority(0)]
+ public async Task TypeAtEndOfMethod(Server server) {
+ var text = @"
+class Fob(object):
+ def oar(self, a):
+ pass
+
+
+ def fob(self):
+ pass
+
+x = Fob()
+x.oar(100)
+";
+
+ var uri = await server.OpenDefaultDocumentAndGetUriAsync(text);
+ var completion = await server.SendCompletion(uri, 5, 8);
+ completion.Should().HaveItem("a")
+ .Which.Should().HaveDocumentation("int");
+ }
+
+ [ServerTestMethod, Priority(0)]
+ public async Task TypeAtEndOfIncompleteMethod(Server server) {
+ var text = @"
+class Fob(object):
+ def oar(self, a):
+
+
+
+
+
+x = Fob()
+x.oar(100)
+";
+
+ var uri = await server.OpenDefaultDocumentAndGetUriAsync(text);
+ var completion = await server.SendCompletion(uri, 5, 8);
+ completion.Should().HaveItem("a")
+ .Which.Should().HaveDocumentation("int");
+ }
+
+ [ServerTestMethod, Priority(0)]
+ public async Task TypeIntersectionUserDefinedTypes(Server server) {
+ var text = @"
+class C1(object):
+ def fob(self): pass
+
+class C2(object):
+ def oar(self): pass
+
+c = C1()
+c.fob()
+c = C2()
+c.
+";
+
+
+ var uri = await server.OpenDefaultDocumentAndGetUriAsync(text);
+ var completion = await server.SendCompletion(uri, 10, 2);
+ completion.Should().NotContainLabels("fob", "oar");
+ }
+
+ [DataRow(@"
+def foo():
+ pass
+
+", 3, 0, "foo", "foo($0)")]
+ [DataRow(@"
+def foo():
+ pass
+fo
+", 3, 2, "foo", "foo($0)")]
+ [DataRow(@"
+def foo(value):
+ pass
+
+", 3, 0, "foo", "foo($0)")]
+ [DataRow(@"
+def foo(value):
+ pass
+
+", 3, 0, "min", "min($0)")]
+ [DataRow(@"
+class Class1(object):
+ def foo(self):
+ pass
+Class1().
+", 4, 9, "foo", "foo($0)")]
+ [DataRow(@"
+class Class1(object):
+ def foo(self, value):
+ pass
+Class1().
+", 4, 9, "foo", "foo($0)")]
+ [DataRow(@"
+class Class1(list):
+ def foo(self):
+ pass
+Class1().
+", 4, 9, "append", "append($0)")]
+ [ServerTestMethod, Priority(0)]
+ public async Task Completion_AddBracketsEnabled(Server server, string code, int row, int character, string expectedLabel, string expectedInsertText) {
+ await server.SendDidChangeConfiguration(new ServerSettings.PythonCompletionOptions {addBrackets = true});
+
+ var uri = await server.OpenDefaultDocumentAndGetUriAsync(code);
+ var completion = await server.SendCompletion(uri, row, character);
+
+ completion.Should().HaveItem(expectedLabel)
+ .Which.Should().HaveInsertText(expectedInsertText);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Analysis/Engine/Test/FluentAssertions/CompletionListAssertions.cs b/src/Analysis/Engine/Test/FluentAssertions/CompletionListAssertions.cs
index 9617df065..c5e7f5dd1 100644
--- a/src/Analysis/Engine/Test/FluentAssertions/CompletionListAssertions.cs
+++ b/src/Analysis/Engine/Test/FluentAssertions/CompletionListAssertions.cs
@@ -42,7 +42,7 @@ public AndConstraint OnlyHaveLabels(IEnumerable i.label).ToArray() ?? new string[0];
var expected = labels.ToArray();
- var errorMessage = GetAssertCollectionOnlyContainsMessage(actual, expected, "completion list items", "label", "labels");
+ var errorMessage = GetAssertCollectionOnlyContainsMessage(actual, expected, GetName(), "label", "labels");
Execute.Assertion.ForCondition(errorMessage == null)
.BecauseOf(because, reasonArgs)
@@ -56,7 +56,7 @@ public AndWhichConstraint HaveItem(str
NotBeNull(because, reasonArgs);
var actual = Subject?.items.Where(i => string.Equals(i.label, label, StringComparison.Ordinal)).ToArray() ?? Array.Empty();
- var errorMessage = GetAssertCollectionContainsMessage(actual.Select(i => i.label).ToArray(), new [] { label }, "completion list items", "label", "labels");
+ var errorMessage = GetAssertCollectionContainsMessage(actual.Select(i => i.label).ToArray(), new [] { label }, GetName(), "label", "labels");
Execute.Assertion.ForCondition(errorMessage == null)
.BecauseOf(because, reasonArgs)
@@ -76,7 +76,7 @@ public AndConstraint HaveLabels(IEnumerable la
var actual = Subject.items?.Select(i => i.label).ToArray() ?? new string[0];
var expected = labels.ToArray();
- var errorMessage = GetAssertCollectionContainsMessage(actual, expected, "completion list items", "label", "labels");
+ var errorMessage = GetAssertCollectionContainsMessage(actual, expected, GetName(), "label", "labels");
Execute.Assertion.ForCondition(errorMessage == null)
.BecauseOf(because, reasonArgs)
@@ -96,7 +96,7 @@ public AndConstraint NotContainLabels(IEnumerable i.label).ToArray() ?? new string[0];
var expected = labels.ToArray();
- var errorMessage = GetAssertCollectionNotContainMessage(actual, expected, "completion list items", "label", "labels");
+ var errorMessage = GetAssertCollectionNotContainMessage(actual, expected, GetName(), "label", "labels");
Execute.Assertion.ForCondition(errorMessage == null)
.BecauseOf(because, reasonArgs)
@@ -104,5 +104,8 @@ public AndConstraint NotContainLabels(IEnumerable(this);
}
+
+ [CustomAssertion]
+ private static string GetName() => CallerIdentifier.DetermineCallerIdentity() ?? "completion list items";
}
}
\ No newline at end of file
diff --git a/src/Analysis/Engine/Test/LanguageServerTests.cs b/src/Analysis/Engine/Test/LanguageServerTests.cs
index fae6da258..4cd1fdc16 100644
--- a/src/Analysis/Engine/Test/LanguageServerTests.cs
+++ b/src/Analysis/Engine/Test/LanguageServerTests.cs
@@ -385,18 +385,6 @@ public async Task CompletionInWithStatement() {
await s.UnloadFileAsync(u);
}
- [TestMethod, Priority(0)]
- public async Task CompletionInWithStatementDerivedClass() {
- using (var server = await CreateServer()) {
- var uri = await server.OpenDefaultDocumentAndGetUriAsync("with open(x) as fs:\n fs. ");
- await server.GetAnalysisAsync(uri);
- var completions = await server.SendCompletion(uri, 1, 5);
-
- completions.Should().HaveLabels("read", "write");
- await server.UnloadFileAsync(uri);
- }
- }
-
[TestMethod, Priority(0)]
public async Task CompletionInImport() {
var s = await CreateServer();
diff --git a/src/Analysis/Engine/Test/ServerExtensions.cs b/src/Analysis/Engine/Test/ServerExtensions.cs
index 08615b5ff..cfc562917 100644
--- a/src/Analysis/Engine/Test/ServerExtensions.cs
+++ b/src/Analysis/Engine/Test/ServerExtensions.cs
@@ -186,6 +186,31 @@ public static Task SendDocumentOnTypeFormatting(this Server server,
}, CancellationToken.None);
}
+ public static Task SendDidChangeConfiguration(this Server server, ServerSettings.PythonCompletionOptions pythonCompletionOptions, int failAfter = 30000) {
+ var currentSettings = server.Settings;
+ var settings = new LanguageServerSettings();
+
+ settings.completion.showAdvancedMembers = pythonCompletionOptions.showAdvancedMembers;
+ settings.completion.addBrackets = pythonCompletionOptions.addBrackets;
+
+ settings.analysis.openFilesOnly = currentSettings.analysis.openFilesOnly;
+ if (currentSettings is LanguageServerSettings languageServerSettings) {
+ settings.diagnosticPublishDelay = languageServerSettings.diagnosticPublishDelay;
+ settings.symbolsHierarchyDepthLimit = languageServerSettings.symbolsHierarchyDepthLimit;
+ }
+
+ var errors = currentSettings.analysis.errors;
+ var warnings = currentSettings.analysis.warnings;
+ var information = currentSettings.analysis.information;
+ var disabled = currentSettings.analysis.disabled;
+ settings.analysis.SetErrorSeverityOptions(errors, warnings, information, disabled);
+
+ return server.SendDidChangeConfiguration(settings, failAfter);
+ }
+
+ public static Task SendDidChangeConfiguration(this Server server, ServerSettings settings, int failAfter = 30000)
+ => server.DidChangeConfiguration(new DidChangeConfigurationParams { settings = settings }, new CancellationTokenSource(failAfter).Token);
+
public static async Task ChangeDefaultDocumentAndGetAnalysisAsync(this Server server, string text, int failAfter = 30000) {
var projectEntry = (ProjectEntry) server.ProjectFiles.Single();
await server.SendDidChangeTextDocumentAsync(projectEntry.DocumentUri, text);
diff --git a/src/Analysis/Engine/Test/ServerTestMethodAttribute.cs b/src/Analysis/Engine/Test/ServerTestMethodAttribute.cs
new file mode 100644
index 000000000..9bc4231d6
--- /dev/null
+++ b/src/Analysis/Engine/Test/ServerTestMethodAttribute.cs
@@ -0,0 +1,69 @@
+// Python Tools for Visual Studio
+// Copyright(c) Microsoft Corporation
+// All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the License); you may not use
+// this file except in compliance with the License. You may obtain a copy of the
+// License at http://www.apache.org/licenses/LICENSE-2.0
+//
+// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
+// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
+// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+// MERCHANTABILITY OR NON-INFRINGEMENT.
+//
+// See the Apache Version 2.0 License for specific language governing
+// permissions and limitations under the License.
+
+using System;
+using System.Threading.Tasks;
+using Microsoft.Python.LanguageServer.Implementation;
+using Microsoft.PythonTools.Analysis;
+using Microsoft.PythonTools.Interpreter;
+using Microsoft.PythonTools.Parsing;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using TestUtilities;
+
+namespace AnalysisTests {
+ public class ServerTestMethodAttribute : TestMethodAttribute {
+ public bool LatestAvailable2X { get; set; }
+ public int VersionArgumentIndex { get; set; } = -1;
+
+ public override TestResult[] Execute(ITestMethod testMethod) {
+ if (testMethod.ParameterTypes[0].ParameterType == typeof(Server) && (testMethod.Arguments == null || testMethod.ParameterTypes.Length == testMethod.Arguments.Length + 1)) {
+ return new[] { ExecuteWithServer(testMethod) };
+ }
+
+ return base.Execute(testMethod);
+ }
+
+ private TestResult ExecuteWithServer(ITestMethod testMethod) {
+ var arguments = ExtendArguments(testMethod.Arguments);
+
+ TestEnvironmentImpl.AddBeforeAfterTest(async () => {
+ var interpreterConfiguration = GetInterpreterConfiguration(arguments);
+ var server = await new Server().InitializeAsync(interpreterConfiguration);
+ arguments[0] = server;
+ return server;
+ });
+
+ return testMethod.Invoke(arguments);
+ }
+
+ private InterpreterConfiguration GetInterpreterConfiguration(object[] arguments) {
+ return VersionArgumentIndex >= 0 && VersionArgumentIndex < arguments.Length
+ ? PythonVersions.GetRequiredCPythonConfiguration((PythonLanguageVersion)arguments[VersionArgumentIndex])
+ : LatestAvailable2X ? PythonVersions.LatestAvailable2X : PythonVersions.LatestAvailable;
+ }
+
+ private static object[] ExtendArguments(object[] arguments) {
+ if (arguments == null || arguments.Length == 0) {
+ return new object[1];
+ }
+
+ var length = arguments.Length;
+ var args = new object[length + 1];
+ Array.Copy(arguments, 0, args, 1, length);
+ return args;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/LanguageServer/Impl/Definitions/ServerSettings.cs b/src/LanguageServer/Impl/Definitions/ServerSettings.cs
index 88fc757a9..9d1319cb6 100644
--- a/src/LanguageServer/Impl/Definitions/ServerSettings.cs
+++ b/src/LanguageServer/Impl/Definitions/ServerSettings.cs
@@ -55,7 +55,9 @@ public void SetErrorSeverityOptions(string[] errors, string[] warnings, string[]
public class PythonCompletionOptions {
public bool showAdvancedMembers = true;
+ public bool addBrackets = false;
}
+
public readonly PythonCompletionOptions completion = new PythonCompletionOptions();
}
}
diff --git a/src/LanguageServer/Impl/Implementation/CompletionAnalysis.cs b/src/LanguageServer/Impl/Implementation/CompletionAnalysis.cs
index 589deb204..509f0c01b 100644
--- a/src/LanguageServer/Impl/Implementation/CompletionAnalysis.cs
+++ b/src/LanguageServer/Impl/Implementation/CompletionAnalysis.cs
@@ -180,7 +180,7 @@ public IEnumerable GetCompletionsFromString(string expr) {
public IEnumerable GetCompletions() {
var opts = Options | GetMemberOptions.ForEval;
- bool allowKeywords = true, allowArguments = true;
+ bool allowKeywords = true;
List additional = null;
var res = GetNoCompletionsInComments() ??
@@ -191,10 +191,10 @@ public IEnumerable GetCompletions() {
GetCompletionsInDefinition(ref allowKeywords, ref additional) ??
GetCompletionsInForStatement() ??
GetCompletionsInWithStatement() ??
- GetCompletionsInRaiseStatement(ref allowArguments, ref opts) ??
+ GetCompletionsInRaiseStatement(ref opts) ??
GetCompletionsInExceptStatement(ref allowKeywords, ref opts) ??
GetCompletionsFromError() ??
- GetCompletionsFromTopLevel(allowKeywords, allowArguments, opts);
+ GetCompletionsFromTopLevel(allowKeywords, opts);
if (additional != null) {
res = res.Concat(additional);
@@ -548,7 +548,7 @@ private IEnumerable GetCompletionsInWithStatement() {
return null;
}
- private IEnumerable GetCompletionsInRaiseStatement(ref bool allowKeywords, ref GetMemberOptions opts) {
+ private IEnumerable GetCompletionsInRaiseStatement(ref GetMemberOptions opts) {
if (Statement is RaiseStatement rs) {
// raise Type, Value, Traceback with Cause
if (rs.Cause != null) {
@@ -751,7 +751,7 @@ private IEnumerable GetCompletionsFromError() {
return null;
}
- private IEnumerable GetCompletionsFromTopLevel(bool allowKeywords, bool allowArguments, GetMemberOptions opts) {
+ private IEnumerable GetCompletionsFromTopLevel(bool allowKeywords, GetMemberOptions opts) {
if (Node?.EndIndex < Index) {
return Empty;
}
@@ -773,22 +773,20 @@ private IEnumerable GetCompletionsFromTopLevel(bool allowKeyword
_log.TraceMessage($"Completing all names");
var members = Analysis.GetAllMembers(Position, opts);
- if (allowArguments) {
- var finder = new ExpressionFinder(Tree, new GetExpressionOptions { Calls = true });
- if (finder.GetExpression(Index) is CallExpression callExpr &&
- callExpr.GetArgumentAtIndex(Tree, Index, out _)) {
- var argNames = Analysis.GetSignatures(callExpr.Target, Position)
- .SelectMany(o => o.Parameters).Select(p => p?.Name)
- .Where(n => !string.IsNullOrEmpty(n))
- .Distinct()
- .Except(callExpr.Args.MaybeEnumerate().Select(a => a.Name).Where(n => !string.IsNullOrEmpty(n)))
- .Select(n => new MemberResult($"{n}=", PythonMemberType.NamedArgument) as IMemberResult);
+ var finder = new ExpressionFinder(Tree, new GetExpressionOptions { Calls = true });
+ if (finder.GetExpression(Index) is CallExpression callExpr &&
+ callExpr.GetArgumentAtIndex(Tree, Index, out _)) {
+ var argNames = Analysis.GetSignatures(callExpr.Target, Position)
+ .SelectMany(o => o.Parameters).Select(p => p?.Name)
+ .Where(n => !string.IsNullOrEmpty(n))
+ .Distinct()
+ .Except(callExpr.Args.MaybeEnumerate().Select(a => a.Name).Where(n => !string.IsNullOrEmpty(n)))
+ .Select(n => new MemberResult($"{n}=", PythonMemberType.NamedArgument) as IMemberResult);
- argNames = argNames.MaybeEnumerate().ToArray();
- _log.TraceMessage($"Including {argNames.Count()} named arguments");
+ argNames = argNames.MaybeEnumerate().ToArray();
+ _log.TraceMessage($"Including {argNames.Count()} named arguments");
- members = members?.Concat(argNames) ?? argNames;
- }
+ members = members?.Concat(argNames) ?? argNames;
}
return members.Select(ToCompletionItem).Where(c => !string.IsNullOrEmpty(c.insertText));
@@ -802,14 +800,7 @@ private IEnumerable GetCompletionsFromTopLevel(bool allowKeyword
private static readonly CompletionItem ImportKeywordCompletion = ToCompletionItem("import", PythonMemberType.Keyword);
private static readonly CompletionItem WithKeywordCompletion = ToCompletionItem("with", PythonMemberType.Keyword);
private static readonly CompletionItem StarCompletion = ToCompletionItem("*", PythonMemberType.Keyword);
-
- private static CompletionItem KeywordCompletion(string keyword) => new CompletionItem {
- label = keyword,
- insertText = keyword,
- kind = CompletionItemKind.Keyword,
- _kind = PythonMemberType.Keyword.ToString().ToLowerInvariant()
- };
-
+
private CompletionItem ToCompletionItem(IMemberResult m) {
var completion = m.Completion;
if (string.IsNullOrEmpty(completion)) {
diff --git a/src/LanguageServer/Impl/Implementation/Server.Completion.cs b/src/LanguageServer/Impl/Implementation/Server.Completion.cs
index f17856b37..e3776cb59 100644
--- a/src/LanguageServer/Impl/Implementation/Server.Completion.cs
+++ b/src/LanguageServer/Impl/Implementation/Server.Completion.cs
@@ -61,8 +61,21 @@ public override async Task Completion(CompletionParams @params,
members = members.Where(m => m.kind == filterKind.Value);
}
+ var completions = members.ToArray();
+ if (Settings.completion.addBrackets && (!filterKind.HasValue || CanHaveBrackets(filterKind.Value))) {
+ foreach (var completionItem in completions.Where(ci => CanHaveBrackets(ci.kind))) {
+ completionItem.insertText += "($0)";
+ completionItem.insertTextFormat = InsertTextFormat.Snippet;
+ }
+ }
+
+ bool CanHaveBrackets(CompletionItemKind kind)
+ => kind == CompletionItemKind.Constructor ||
+ kind == CompletionItemKind.Function ||
+ kind == CompletionItemKind.Method;
+
var res = new CompletionList {
- items = members.ToArray(),
+ items = completions,
_expr = ctxt.ParentExpression?.ToCodeString(tree, CodeFormattingOptions.Traditional),
_commitByDefault = ctxt.ShouldCommitByDefault,
_allowSnippet = ctxt.ShouldAllowSnippets
diff --git a/src/LanguageServer/Impl/LanguageServer.cs b/src/LanguageServer/Impl/LanguageServer.cs
index 425475bba..d77d055d0 100644
--- a/src/LanguageServer/Impl/LanguageServer.cs
+++ b/src/LanguageServer/Impl/LanguageServer.cs
@@ -138,6 +138,7 @@ public async Task DidChangeConfiguration(JToken token, CancellationToken cancell
var autoComplete = pythonSection["autoComplete"];
settings.completion.showAdvancedMembers = GetSetting(autoComplete, "showAdvancedMembers", true);
+ settings.completion.addBrackets = GetSetting(autoComplete, "addBrackets", false);
var analysis = pythonSection["analysis"];
settings.analysis.openFilesOnly = GetSetting(analysis, "openFilesOnly", false);
@@ -175,7 +176,7 @@ public async Task DidChangeWatchedFiles(JToken token, CancellationToken cancella
[JsonRpcMethod("workspace/symbol")]
public async Task WorkspaceSymbols(JToken token, CancellationToken cancellationToken) {
- await _prioritizer.DefaultPriorityAsync();
+ await _prioritizer.DefaultPriorityAsync(cancellationToken);
return await _server.WorkspaceSymbols(ToObject(token), cancellationToken);
}
diff --git a/src/UnitTests/Core/Impl/TestEnvironmentImpl.cs b/src/UnitTests/Core/Impl/TestEnvironmentImpl.cs
index f9604994e..844c8659e 100644
--- a/src/UnitTests/Core/Impl/TestEnvironmentImpl.cs
+++ b/src/UnitTests/Core/Impl/TestEnvironmentImpl.cs
@@ -15,6 +15,7 @@
// permissions and limitations under the License.
using System;
+using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
@@ -27,7 +28,11 @@ public class TestEnvironmentImpl {
public static TimeSpan Elapsed() => Instance?._stopwatch.Value?.Elapsed ?? new TimeSpan();
public static void TestInitialize(string testFullName, int secondsTimeout = 10) => Instance?.BeforeTestRun(testFullName, secondsTimeout);
public static void TestCleanup() => Instance?.AfterTestRun();
+ public static void AddBeforeAfterTest(Func> beforeAfterTest) => Instance?.AddBeforeAfterTestAction(() => beforeAfterTest().GetAwaiter().GetResult());
+ public static void AddBeforeAfterTest(Func beforeAfterTest) => Instance?.AddBeforeAfterTestAction(beforeAfterTest);
+ private readonly AsyncLocal>> _beforeAfterTestActions = new AsyncLocal>>();
+ private readonly AsyncLocal> _beforeAfterTestActionDisposables = new AsyncLocal>();
private readonly AsyncLocal _taskObserver = new AsyncLocal();
private readonly AsyncLocal _stopwatch = new AsyncLocal();
private readonly AssemblyLoader _assemblyLoader = new AssemblyLoader();
@@ -55,14 +60,40 @@ protected virtual void BeforeTestRun(string testFullName, int secondsTimeout) {
throw new InvalidOperationException("AsyncLocal reentrancy");
}
+ var beforeAfterTestActions = _beforeAfterTestActions.Value;
+ _beforeAfterTestActions.Value = null;
+
_taskObserver.Value = new TaskObserver(secondsTimeout);
_stopwatch.Value = new Stopwatch();
_stopwatch.Value.Start();
TestData.SetTestRunScope(testFullName);
+
+ if (beforeAfterTestActions != null) {
+ var disposables = new Stack();
+ try {
+ foreach (var beforeAfterTestAction in beforeAfterTestActions) {
+ disposables.Push(beforeAfterTestAction());
+ }
+ } catch (Exception) {
+ RunDisposablesSafe(disposables);
+ throw;
+ }
+
+ _beforeAfterTestActionDisposables.Value = disposables;
+ }
}
protected virtual void AfterTestRun() {
try {
+ var disposables = _beforeAfterTestActionDisposables.Value;
+ _beforeAfterTestActionDisposables.Value = null;
+ if (disposables != null) {
+ var afterTestRunException = RunDisposablesSafe(disposables);
+ if (afterTestRunException != null) {
+ throw afterTestRunException;
+ }
+ }
+
_taskObserver.Value?.WaitForObservedTask();
_stopwatch.Value?.Stop();
TestData.ClearTestRunScope();
@@ -71,5 +102,24 @@ protected virtual void AfterTestRun() {
_taskObserver.Value = null;
}
}
+
+ private void AddBeforeAfterTestAction(Func beforeAfterTest) {
+ var actions = _beforeAfterTestActions.Value ?? (_beforeAfterTestActions.Value = new List>());
+ actions.Add(beforeAfterTest);
+ }
+
+ private AggregateException RunDisposablesSafe(Stack disposables) {
+ var exceptions = new List();
+ while (disposables.Count > 0) {
+ var disposable = disposables.Pop();
+ try {
+ disposable.Dispose();
+ } catch (Exception ex) {
+ exceptions.Add(ex);
+ }
+ }
+
+ return exceptions.Count > 0 ? new AggregateException(exceptions) : null;
+ }
}
}
\ No newline at end of file