From ac92d3731781b269cb26a26b9666b8d3ab526f28 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Mon, 20 May 2019 16:57:28 -0700 Subject: [PATCH] look up __init__ in signature help if call is on a class, pass correct name through to tooltip --- .../Impl/Definitions/IDocumentationSource.cs | 2 +- .../Sources/MarkdownDocumentationSource.cs | 4 +- .../Sources/PlainTextDocumentationSource.cs | 4 +- .../Impl/Sources/SignatureSource.cs | 25 ++++++-- src/LanguageServer/Test/SignatureTests.cs | 58 +++++++++++++++++++ 5 files changed, 83 insertions(+), 10 deletions(-) diff --git a/src/LanguageServer/Impl/Definitions/IDocumentationSource.cs b/src/LanguageServer/Impl/Definitions/IDocumentationSource.cs index 5552827ee..e87c77b9d 100644 --- a/src/LanguageServer/Impl/Definitions/IDocumentationSource.cs +++ b/src/LanguageServer/Impl/Definitions/IDocumentationSource.cs @@ -20,7 +20,7 @@ namespace Microsoft.Python.LanguageServer { public interface IDocumentationSource { InsertTextFormat DocumentationFormat { get; } MarkupContent GetHover(string name, IMember member, IPythonType self = null); - string GetSignatureString(IPythonFunctionType ft, IPythonType self, int overloadIndex = 0); + string GetSignatureString(IPythonFunctionType ft, IPythonType self, int overloadIndex = 0, string name = null); MarkupContent FormatParameterDocumentation(IParameterInfo parameter); MarkupContent FormatDocumentation(string documentation); } diff --git a/src/LanguageServer/Impl/Sources/MarkdownDocumentationSource.cs b/src/LanguageServer/Impl/Sources/MarkdownDocumentationSource.cs index 6074ded5d..342804980 100644 --- a/src/LanguageServer/Impl/Sources/MarkdownDocumentationSource.cs +++ b/src/LanguageServer/Impl/Sources/MarkdownDocumentationSource.cs @@ -73,7 +73,7 @@ public MarkupContent FormatDocumentation(string documentation) { return new MarkupContent { kind = MarkupKind.Markdown, value = DocstringConverter.ToMarkdown(documentation) }; } - public string GetSignatureString(IPythonFunctionType ft, IPythonType self, int overloadIndex = 0) { + public string GetSignatureString(IPythonFunctionType ft, IPythonType self, int overloadIndex = 0, string name = null) { var o = ft.Overloads[overloadIndex]; var parms = GetFunctionParameters(ft); @@ -81,7 +81,7 @@ public string GetSignatureString(IPythonFunctionType ft, IPythonType self, int o var returnDoc = o.GetReturnDocumentation(self); var annString = string.IsNullOrEmpty(returnDoc) ? string.Empty : $" -> {returnDoc}"; - return $"{ft.Name}({parmString}){annString}"; + return $"{name ?? ft.Name}({parmString}){annString}"; } public MarkupContent FormatParameterDocumentation(IParameterInfo parameter) { diff --git a/src/LanguageServer/Impl/Sources/PlainTextDocumentationSource.cs b/src/LanguageServer/Impl/Sources/PlainTextDocumentationSource.cs index 46213d055..3a508858f 100644 --- a/src/LanguageServer/Impl/Sources/PlainTextDocumentationSource.cs +++ b/src/LanguageServer/Impl/Sources/PlainTextDocumentationSource.cs @@ -73,7 +73,7 @@ public MarkupContent FormatDocumentation(string documentation) { return new MarkupContent { kind = MarkupKind.PlainText, value = documentation }; } - public string GetSignatureString(IPythonFunctionType ft, IPythonType self, int overloadIndex = 0) { + public string GetSignatureString(IPythonFunctionType ft, IPythonType self, int overloadIndex = 0, string name = null) { var o = ft.Overloads[overloadIndex]; var parms = GetFunctionParameters(ft); @@ -81,7 +81,7 @@ public string GetSignatureString(IPythonFunctionType ft, IPythonType self, int o var returnDoc = o.GetReturnDocumentation(self); var annString = string.IsNullOrEmpty(returnDoc) ? string.Empty : $" -> {returnDoc}"; - return $"{ft.Name}({parmString}){annString}"; + return $"{name ?? ft.Name}({parmString}){annString}"; } public MarkupContent FormatParameterDocumentation(IParameterInfo parameter) { diff --git a/src/LanguageServer/Impl/Sources/SignatureSource.cs b/src/LanguageServer/Impl/Sources/SignatureSource.cs index 3fd2b2276..298ac5e80 100644 --- a/src/LanguageServer/Impl/Sources/SignatureSource.cs +++ b/src/LanguageServer/Impl/Sources/SignatureSource.cs @@ -42,18 +42,33 @@ public SignatureHelp GetSignature(IDocumentAnalysis analysis, SourceLocation loc IMember value = null; IPythonType selfType = null; + string name = null; var call = node as CallExpression; if (call != null) { using (analysis.ExpressionEvaluator.OpenScope(analysis.Document, scope)) { - if (call.Target is MemberExpression mex) { - var v = analysis.ExpressionEvaluator.GetValueFromExpression(mex.Target); - selfType = v?.GetPythonType(); + switch (call.Target) { + case MemberExpression mex: + var v = analysis.ExpressionEvaluator.GetValueFromExpression(mex.Target); + selfType = v?.GetPythonType(); + name = mex.Name; + break; + case NameExpression ne: + name = ne.Name; + break; } + value = analysis.ExpressionEvaluator.GetValueFromExpression(call.Target); } } - var ft = value?.GetPythonType(); + IPythonFunctionType ft; + + if (value is IPythonClassType cls) { + ft = cls.GetMember("__init__"); + } else { + ft = value?.GetPythonType(); + } + if (ft == null) { return null; } @@ -70,7 +85,7 @@ public SignatureHelp GetSignature(IDocumentAnalysis analysis, SourceLocation loc }).ToArray(); signatures[i] = new SignatureInformation { - label = _docSource.GetSignatureString(ft, selfType, i), + label = _docSource.GetSignatureString(ft, selfType, i, name), documentation = _docSource.FormatDocumentation(ft.Documentation), parameters = parameters }; diff --git a/src/LanguageServer/Test/SignatureTests.cs b/src/LanguageServer/Test/SignatureTests.cs index adadc8d43..f95954c81 100644 --- a/src/LanguageServer/Test/SignatureTests.cs +++ b/src/LanguageServer/Test/SignatureTests.cs @@ -13,8 +13,11 @@ // See the Apache Version 2.0 License for specific language governing // permissions and limitations under the License. +using System.IO; using System.Threading.Tasks; using FluentAssertions; +using Microsoft.Python.Analysis.Analyzer; +using Microsoft.Python.Analysis.Documents; using Microsoft.Python.Core.Text; using Microsoft.Python.LanguageServer.Sources; using Microsoft.Python.Parsing.Tests; @@ -52,6 +55,61 @@ def method(self, a:int, b) -> float: sig.signatures[0].label.Should().Be("method(a: int, b) -> float"); } + [TestMethod, Priority(0)] + public async Task ClassInitializer() { + const string code = @" +class C: + def __init__(self, a:int, b): + pass + +C() +"; + var analysis = await GetAnalysisAsync(code); + var src = new SignatureSource(new PlainTextDocumentationSource()); + + var sig = src.GetSignature(analysis, new SourceLocation(6, 3)); + sig.activeSignature.Should().Be(0); + sig.activeParameter.Should().Be(0); + sig.signatures.Length.Should().Be(1); + sig.signatures[0].label.Should().Be("C(a: int, b)"); + } + + [TestMethod, Priority(0)] + public async Task ImportedClassInitializer() { + const string module1Code = @" +class C: + def __init__(self, a:int, b): + pass +"; + + const string appCode = @" +import module1 + +module1.C() +"; + var module1Uri = TestData.GetTestSpecificUri("module1.py"); + var appUri = TestData.GetTestSpecificUri("app.py"); + + var root = Path.GetDirectoryName(appUri.AbsolutePath); + await CreateServicesAsync(root, PythonVersions.LatestAvailable3X); + var rdt = Services.GetService(); + var analyzer = Services.GetService(); + + rdt.OpenDocument(module1Uri, module1Code); + + var app = rdt.OpenDocument(appUri, appCode); + await analyzer.WaitForCompleteAnalysisAsync(); + var analysis = await app.GetAnalysisAsync(-1); + + var src = new SignatureSource(new PlainTextDocumentationSource()); + + var sig = src.GetSignature(analysis, new SourceLocation(4, 11)); + sig.activeSignature.Should().Be(0); + sig.activeParameter.Should().Be(0); + sig.signatures.Length.Should().Be(1); + sig.signatures[0].label.Should().Be("C(a: int, b)"); + } + [TestMethod, Priority(0)] public async Task GenericClassMethod() { const string code = @"