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

Commit f3ffe2c

Browse files
authored
Handle calls on classes for signature help (#1107)
1 parent 2e3c1c9 commit f3ffe2c

5 files changed

Lines changed: 83 additions & 10 deletions

File tree

src/LanguageServer/Impl/Definitions/IDocumentationSource.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ namespace Microsoft.Python.LanguageServer {
2020
public interface IDocumentationSource {
2121
InsertTextFormat DocumentationFormat { get; }
2222
MarkupContent GetHover(string name, IMember member, IPythonType self = null);
23-
string GetSignatureString(IPythonFunctionType ft, IPythonType self, int overloadIndex = 0);
23+
string GetSignatureString(IPythonFunctionType ft, IPythonType self, int overloadIndex = 0, string name = null);
2424
MarkupContent FormatParameterDocumentation(IParameterInfo parameter);
2525
MarkupContent FormatDocumentation(string documentation);
2626
}

src/LanguageServer/Impl/Sources/MarkdownDocumentationSource.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,15 +73,15 @@ public MarkupContent FormatDocumentation(string documentation) {
7373
return new MarkupContent { kind = MarkupKind.Markdown, value = DocstringConverter.ToMarkdown(documentation) };
7474
}
7575

76-
public string GetSignatureString(IPythonFunctionType ft, IPythonType self, int overloadIndex = 0) {
76+
public string GetSignatureString(IPythonFunctionType ft, IPythonType self, int overloadIndex = 0, string name = null) {
7777
var o = ft.Overloads[overloadIndex];
7878

7979
var parms = GetFunctionParameters(ft);
8080
var parmString = string.Join(", ", parms);
8181
var returnDoc = o.GetReturnDocumentation(self);
8282
var annString = string.IsNullOrEmpty(returnDoc) ? string.Empty : $" -> {returnDoc}";
8383

84-
return $"{ft.Name}({parmString}){annString}";
84+
return $"{name ?? ft.Name}({parmString}){annString}";
8585
}
8686

8787
public MarkupContent FormatParameterDocumentation(IParameterInfo parameter) {

src/LanguageServer/Impl/Sources/PlainTextDocumentationSource.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,15 +73,15 @@ public MarkupContent FormatDocumentation(string documentation) {
7373
return new MarkupContent { kind = MarkupKind.PlainText, value = documentation };
7474
}
7575

76-
public string GetSignatureString(IPythonFunctionType ft, IPythonType self, int overloadIndex = 0) {
76+
public string GetSignatureString(IPythonFunctionType ft, IPythonType self, int overloadIndex = 0, string name = null) {
7777
var o = ft.Overloads[overloadIndex];
7878

7979
var parms = GetFunctionParameters(ft);
8080
var parmString = string.Join(", ", parms);
8181
var returnDoc = o.GetReturnDocumentation(self);
8282
var annString = string.IsNullOrEmpty(returnDoc) ? string.Empty : $" -> {returnDoc}";
8383

84-
return $"{ft.Name}({parmString}){annString}";
84+
return $"{name ?? ft.Name}({parmString}){annString}";
8585
}
8686

8787
public MarkupContent FormatParameterDocumentation(IParameterInfo parameter) {

src/LanguageServer/Impl/Sources/SignatureSource.cs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,33 @@ public SignatureHelp GetSignature(IDocumentAnalysis analysis, SourceLocation loc
4242

4343
IMember value = null;
4444
IPythonType selfType = null;
45+
string name = null;
4546
var call = node as CallExpression;
4647
if (call != null) {
4748
using (analysis.ExpressionEvaluator.OpenScope(analysis.Document, scope)) {
48-
if (call.Target is MemberExpression mex) {
49-
var v = analysis.ExpressionEvaluator.GetValueFromExpression(mex.Target);
50-
selfType = v?.GetPythonType();
49+
switch (call.Target) {
50+
case MemberExpression mex:
51+
var v = analysis.ExpressionEvaluator.GetValueFromExpression(mex.Target);
52+
selfType = v?.GetPythonType();
53+
name = mex.Name;
54+
break;
55+
case NameExpression ne:
56+
name = ne.Name;
57+
break;
5158
}
59+
5260
value = analysis.ExpressionEvaluator.GetValueFromExpression(call.Target);
5361
}
5462
}
5563

56-
var ft = value?.GetPythonType<IPythonFunctionType>();
64+
IPythonFunctionType ft;
65+
66+
if (value is IPythonClassType cls) {
67+
ft = cls.GetMember<IPythonFunctionType>("__init__");
68+
} else {
69+
ft = value?.GetPythonType<IPythonFunctionType>();
70+
}
71+
5772
if (ft == null) {
5873
return null;
5974
}
@@ -70,7 +85,7 @@ public SignatureHelp GetSignature(IDocumentAnalysis analysis, SourceLocation loc
7085
}).ToArray();
7186

7287
signatures[i] = new SignatureInformation {
73-
label = _docSource.GetSignatureString(ft, selfType, i),
88+
label = _docSource.GetSignatureString(ft, selfType, i, name),
7489
documentation = _docSource.FormatDocumentation(ft.Documentation),
7590
parameters = parameters
7691
};

src/LanguageServer/Test/SignatureTests.cs

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

16+
using System.IO;
1617
using System.Threading.Tasks;
1718
using FluentAssertions;
19+
using Microsoft.Python.Analysis.Analyzer;
20+
using Microsoft.Python.Analysis.Documents;
1821
using Microsoft.Python.Core.Text;
1922
using Microsoft.Python.LanguageServer.Sources;
2023
using Microsoft.Python.Parsing.Tests;
@@ -52,6 +55,61 @@ def method(self, a:int, b) -> float:
5255
sig.signatures[0].label.Should().Be("method(a: int, b) -> float");
5356
}
5457

58+
[TestMethod, Priority(0)]
59+
public async Task ClassInitializer() {
60+
const string code = @"
61+
class C:
62+
def __init__(self, a:int, b):
63+
pass
64+
65+
C()
66+
";
67+
var analysis = await GetAnalysisAsync(code);
68+
var src = new SignatureSource(new PlainTextDocumentationSource());
69+
70+
var sig = src.GetSignature(analysis, new SourceLocation(6, 3));
71+
sig.activeSignature.Should().Be(0);
72+
sig.activeParameter.Should().Be(0);
73+
sig.signatures.Length.Should().Be(1);
74+
sig.signatures[0].label.Should().Be("C(a: int, b)");
75+
}
76+
77+
[TestMethod, Priority(0)]
78+
public async Task ImportedClassInitializer() {
79+
const string module1Code = @"
80+
class C:
81+
def __init__(self, a:int, b):
82+
pass
83+
";
84+
85+
const string appCode = @"
86+
import module1
87+
88+
module1.C()
89+
";
90+
var module1Uri = TestData.GetTestSpecificUri("module1.py");
91+
var appUri = TestData.GetTestSpecificUri("app.py");
92+
93+
var root = Path.GetDirectoryName(appUri.AbsolutePath);
94+
await CreateServicesAsync(root, PythonVersions.LatestAvailable3X);
95+
var rdt = Services.GetService<IRunningDocumentTable>();
96+
var analyzer = Services.GetService<IPythonAnalyzer>();
97+
98+
rdt.OpenDocument(module1Uri, module1Code);
99+
100+
var app = rdt.OpenDocument(appUri, appCode);
101+
await analyzer.WaitForCompleteAnalysisAsync();
102+
var analysis = await app.GetAnalysisAsync(-1);
103+
104+
var src = new SignatureSource(new PlainTextDocumentationSource());
105+
106+
var sig = src.GetSignature(analysis, new SourceLocation(4, 11));
107+
sig.activeSignature.Should().Be(0);
108+
sig.activeParameter.Should().Be(0);
109+
sig.signatures.Length.Should().Be(1);
110+
sig.signatures[0].label.Should().Be("C(a: int, b)");
111+
}
112+
55113
[TestMethod, Priority(0)]
56114
public async Task GenericClassMethod() {
57115
const string code = @"

0 commit comments

Comments
 (0)