diff --git a/include/swift/IDE/SignatureHelpFormatter.h b/include/swift/IDE/SignatureHelpFormatter.h new file mode 100644 index 0000000000000..03e3bf43b72a6 --- /dev/null +++ b/include/swift/IDE/SignatureHelpFormatter.h @@ -0,0 +1,85 @@ +//===--- SignatureHelpFormatter.h --- ---------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_IDE_SIGNATURE_HELP_FORMATTER_H +#define SWIFT_IDE_SIGNATURE_HELP_FORMATTER_H + +#include "swift/IDE/SignatureHelp.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Allocator.h" + +namespace swift { + +class DeclContext; + +namespace ide { + +class CodeCompletionString; + +struct FormattedSignatureHelp { + struct Parameter { + /// The offset of the parameter text in the signature text. + unsigned Offset; + + /// The length of the parameter text in the signature text. + unsigned Length; + + /// The internal parameter name. + llvm::StringRef Name; + + Parameter() {} + }; + + struct Signature { + llvm::StringRef Text; + llvm::StringRef DocComment; + std::optional ActiveParam; + llvm::ArrayRef Params; + + Signature(llvm::StringRef Text, llvm::StringRef DocComment, + std::optional ActiveParam, + llvm::ArrayRef Params) + : Text(Text), DocComment(DocComment), ActiveParam(ActiveParam), + Params(Params) {} + }; + + llvm::ArrayRef Signatures; + unsigned ActiveSignature; + + FormattedSignatureHelp(llvm::ArrayRef Signatures, + unsigned ActiveSignature) + : Signatures(Signatures), ActiveSignature(ActiveSignature) {} +}; + +class SignatureHelpFormatter { +private: + llvm::BumpPtrAllocator &Allocator; + +public: + SignatureHelpFormatter(llvm::BumpPtrAllocator &Allocator) + : Allocator(Allocator) {} + + FormattedSignatureHelp format(ide::SignatureHelpResult Result); + +private: + FormattedSignatureHelp::Signature + formatSignature(const DeclContext *DC, const ide::Signature &Signature); + + CodeCompletionString *createSignatureString(const ide::Signature &Signature, + const DeclContext *DC); +}; + +} // namespace ide +} // namespace swift + +#endif // SWIFT_IDE_SIGNATURE_HELP_FORMATTER_H diff --git a/lib/IDE/CMakeLists.txt b/lib/IDE/CMakeLists.txt index 9b2829d2bbaa5..cd6387ada7c0a 100644 --- a/lib/IDE/CMakeLists.txt +++ b/lib/IDE/CMakeLists.txt @@ -19,6 +19,7 @@ add_swift_host_library(swiftIDE STATIC CompletionOverrideLookup.cpp ConformingMethodList.cpp SignatureHelp.cpp + SignatureHelpFormatter.cpp CursorInfo.cpp ExprCompletion.cpp ExprContextAnalysis.cpp diff --git a/lib/IDE/CodeCompletionResultBuilder.h b/lib/IDE/CodeCompletionResultBuilder.h index f454cab893074..086057c3856b6 100644 --- a/lib/IDE/CodeCompletionResultBuilder.h +++ b/lib/IDE/CodeCompletionResultBuilder.h @@ -13,11 +13,11 @@ #ifndef SWIFT_LIB_IDE_CODE_COMPLETION_RESULT_BUILDER_H #define SWIFT_LIB_IDE_CODE_COMPLETION_RESULT_BUILDER_H +#include "CodeCompletionStringBuilder.h" #include "swift/AST/Types.h" #include "swift/Basic/LLVM.h" #include "swift/IDE/CodeCompletionResult.h" #include "swift/IDE/CodeCompletionResultSink.h" -#include "swift/IDE/CodeCompletionStringBuilder.h" namespace clang { class Module; diff --git a/lib/IDE/CodeCompletionStringBuilder.cpp b/lib/IDE/CodeCompletionStringBuilder.cpp index 0379a5905c8b8..213dcdfed9a72 100644 --- a/lib/IDE/CodeCompletionStringBuilder.cpp +++ b/lib/IDE/CodeCompletionStringBuilder.cpp @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// -#include "swift/IDE/CodeCompletionStringBuilder.h" +#include "CodeCompletionStringBuilder.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" #include "swift/AST/GenericEnvironment.h" diff --git a/include/swift/IDE/CodeCompletionStringBuilder.h b/lib/IDE/CodeCompletionStringBuilder.h similarity index 100% rename from include/swift/IDE/CodeCompletionStringBuilder.h rename to lib/IDE/CodeCompletionStringBuilder.h diff --git a/lib/IDE/CodeCompletionStringPrinter.cpp b/lib/IDE/CodeCompletionStringPrinter.cpp index 5c7853a9e32cf..a739b9ca7869a 100644 --- a/lib/IDE/CodeCompletionStringPrinter.cpp +++ b/lib/IDE/CodeCompletionStringPrinter.cpp @@ -11,9 +11,9 @@ //===----------------------------------------------------------------------===// #include "swift/IDE/CodeCompletionStringPrinter.h" +#include "CodeCompletionStringBuilder.h" #include "swift/AST/Module.h" #include "swift/Basic/Assertions.h" -#include "swift/IDE/CodeCompletionStringBuilder.h" using namespace swift; using namespace swift::ide; diff --git a/lib/IDE/SignatureHelpFormatter.cpp b/lib/IDE/SignatureHelpFormatter.cpp new file mode 100644 index 0000000000000..528a7c668ddbd --- /dev/null +++ b/lib/IDE/SignatureHelpFormatter.cpp @@ -0,0 +1,204 @@ +//===--- SignatureHelpFormatter.cpp --- -------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/IDE/SignatureHelpFormatter.h" +#include "CodeCompletionStringBuilder.h" +#include "swift/AST/ParameterList.h" +#include "swift/IDE/CommentConversion.h" + +using namespace swift; +using namespace swift::ide; + +using ChunkKind = CodeCompletionString::Chunk::ChunkKind; + +/// \returns Array of parameters of \p VD accounting for implicitly curried +/// instance methods. +static ArrayRef +getParameterArray(const ValueDecl *VD, bool IsImplicitlyCurried, + const ParamDecl *&Scratch) { + if (!VD) + return {}; + + if (IsImplicitlyCurried) { + auto *FD = dyn_cast(VD); + assert(FD && FD->hasImplicitSelfDecl()); + + Scratch = FD->getImplicitSelfDecl(); + return ArrayRef(&Scratch, 1); + } + + if (auto *ParamList = VD->getParameterList()) + return ParamList->getArray(); + + return {}; +} + +static StringRef copyAndClearString(llvm::BumpPtrAllocator &Allocator, + SmallVectorImpl &Str) { + auto Ref = StringRef(Str.data(), Str.size()).copy(Allocator); + Str.clear(); + return Ref; +} + +CodeCompletionString * +SignatureHelpFormatter::createSignatureString(const ide::Signature &Signature, + const DeclContext *DC) { + ValueDecl *FD = Signature.FuncD; + AnyFunctionType *AFT = Signature.FuncTy; + + GenericSignature GenericSig; + if (FD) { + if (auto *GC = FD->getAsGenericContext()) + GenericSig = GC->getGenericSignature(); + } + + CodeCompletionStringBuilder StringBuilder( + Allocator, /*AnnotateResults=*/false, + /*UnderscoreEmptyArgumentLabel=*/!Signature.IsSubscript, + /*FullParameterFlags=*/true); + + DeclBaseName BaseName; + + if (!Signature.IsSecondApply && FD) { + BaseName = FD->getBaseName(); + } else if (Signature.IsSubscript) { + BaseName = DeclBaseName::createSubscript(); + } + + if (!BaseName.empty()) + StringBuilder.addValueBaseName(BaseName, + /*IsMember=*/bool(Signature.BaseType)); + + StringBuilder.addLeftParen(); + + const ParamDecl *ParamScratch; + StringBuilder.addCallArgumentPatterns( + AFT->getParams(), + getParameterArray(FD, Signature.IsImplicitlyCurried, ParamScratch), DC, + GenericSig, DefaultArgumentOutputMode::All, + /*includeDefaultValues=*/true); + + StringBuilder.addRightParen(); + + if (!Signature.IsImplicitlyCurried) { + if (Signature.IsSecondApply) { + // For a second apply, we don't pass the declaration to avoid adding + // incorrect rethrows and reasync which are only usable in a single apply. + StringBuilder.addEffectsSpecifiers(AFT, /*AFD=*/nullptr); + } else { + StringBuilder.addEffectsSpecifiers( + AFT, dyn_cast_or_null(FD)); + } + } + + if (FD && FD->isImplicitlyUnwrappedOptional()) { + StringBuilder.addTypeAnnotationForImplicitlyUnwrappedOptional( + AFT->getResult(), DC, GenericSig); + } else { + StringBuilder.addTypeAnnotation(AFT->getResult(), DC, GenericSig); + } + + return StringBuilder.createCompletionString(); +} + +FormattedSignatureHelp::Signature +SignatureHelpFormatter::formatSignature(const DeclContext *DC, + const ide::Signature &Signature) { + auto *FD = Signature.FuncD; + auto *AFT = Signature.FuncTy; + + bool IsConstructor = isa_and_nonnull(FD); + + auto *SignatureString = createSignatureString(Signature, DC); + + llvm::SmallString<512> SS; + llvm::raw_svector_ostream OS(SS); + + bool SkipResult = AFT->getResult()->isVoid() || IsConstructor; + + SmallVector FormattedParams; + + auto Chunks = SignatureString->getChunks(); + auto C = Chunks.begin(); + while (C != Chunks.end()) { + if (C->is(ChunkKind::TypeAnnotation) && SkipResult) { + ++C; + continue; + } + + if (C->is(ChunkKind::TypeAnnotation)) + OS << " -> "; + + if (C->is(ChunkKind::CallArgumentBegin)) { + unsigned NestingLevel = C->getNestingLevel(); + ++C; + + auto &P = FormattedParams.emplace_back(); + P.Offset = SS.size(); + + do { + if (!C->is(ChunkKind::CallArgumentClosureType) && C->hasText()) + OS << C->getText(); + + ++C; + } while (C != Chunks.end() && !C->endsPreviousNestedGroup(NestingLevel)); + + P.Length = SS.size() - P.Offset; + continue; + } + + if (C->hasText()) + OS << C->getText(); + + ++C; + } + + StringRef SignatureText = copyAndClearString(Allocator, SS); + + // Parameter names. + const ParamDecl *ParamScratch; + auto ParamDecls = + getParameterArray(FD, Signature.IsImplicitlyCurried, ParamScratch); + + if (!ParamDecls.empty()) { + for (unsigned i = 0; i < FormattedParams.size(); ++i) { + FormattedParams[i].Name = ParamDecls[i]->getParameterName().str(); + } + } + + // Documentation. + StringRef DocComment; + if (FD) { + ide::getRawDocumentationComment(FD, OS); + DocComment = copyAndClearString(Allocator, SS); + } + + return FormattedSignatureHelp::Signature( + SignatureText, DocComment, Signature.ParamIdx, + ArrayRef(FormattedParams).copy(Allocator)); +} + +FormattedSignatureHelp +SignatureHelpFormatter::format(SignatureHelpResult Result) { + SmallVector FormattedSignatures; + FormattedSignatures.reserve(Result.Signatures.size()); + + for (auto &Signature : Result.Signatures) { + FormattedSignatures.push_back(formatSignature(Result.DC, Signature)); + } + + // FIXME: Ideally we should select an active signature based on the context. + unsigned ActiveSignature = 0; + + return FormattedSignatureHelp(ArrayRef(FormattedSignatures).copy(Allocator), + ActiveSignature); +} diff --git a/test/IDE/signature_help_closure_param.swift b/test/IDE/signature_help_closure_param.swift new file mode 100644 index 0000000000000..9a04515e53d88 --- /dev/null +++ b/test/IDE/signature_help_closure_param.swift @@ -0,0 +1,7 @@ +// RUN: %target-swift-ide-test -signature-help -code-completion-token=CLOSURE_PARAM -source-filename=%s | %FileCheck %s --check-prefix=CLOSURE_PARAM + +func apply(value: Value, body: (Value) -> Result) -> Result { + return body(#^CLOSURE_PARAM^#) + // CLOSURE_PARAM: Begin signatures, 1 items + // CLOSURE_PARAM-DAG: Signature[Active]: body(Value) -> Result +} diff --git a/test/IDE/signature_help_currying.swift b/test/IDE/signature_help_currying.swift new file mode 100644 index 0000000000000..910a781f37a38 --- /dev/null +++ b/test/IDE/signature_help_currying.swift @@ -0,0 +1,74 @@ +// RUN: %target-swift-ide-test -signature-help -code-completion-token=CURRY_TOPLEVEL -source-filename=%s | %FileCheck %s --check-prefix=CURRY_TOPLEVEL +// RUN: %target-swift-ide-test -signature-help -code-completion-token=CURRY_MEMBER_PARTIAL -source-filename=%s | %FileCheck %s --check-prefix=CURRY_MEMBER_PARTIAL +// RUN: %target-swift-ide-test -signature-help -code-completion-token=CURRY_MEMBER_FULL -source-filename=%s | %FileCheck %s --check-prefix=CURRY_MEMBER_FULL + +struct Adder { + func add(_ x: Int, to y: Int) -> Int { + return x + y + } + + func add(oneTo x: inout Int) { + x += 1 + } + + func add(_ x: T, to y: T) -> T { + return x + y + } + + func add(first: Double!, second: Float, third: Int) -> Double { + return first + Double(second) + Double(third) + } + + func add(arg1 param1: Double, arg2: Float, arg3 param3: Int) -> Double { + return param1 + Double(arg2) + Double(param3) + } + + func add(numbers: Double...) -> Double { + return numbers.reduce(into: 0) { $0 += $1 } + } + + func add(x: Int, y: Int, with adder: (Int, Int) throws -> Int) rethrows -> Int! { + return try adder(x, y) + } + + func add(x: Int) -> (Int) -> Int { + return { (y: Int) in x + y } + } +} + +func topLevelCurried(x: Int) -> (Double) -> (String) -> Void { + fatalError() +} + +func testCurryTopLevel() { + topLevelCurried(x: 1)(#^CURRY_TOPLEVEL^#) + // CURRY_TOPLEVEL: Begin signatures, 1 items + // CURRY_TOPLEVEL-DAG: Signature[Active]: (Double) -> (String) -> Void +} + +func testCurryMemberPartial() { + Adder.add(#^CURRY_MEMBER_PARTIAL^#) + // CURRY_MEMBER_PARTIAL: Begin signatures, 8 items + // CURRY_MEMBER_PARTIAL-DAG: Signature[Active]: add(_ self: Adder) -> (Int, Int) -> Int + // CURRY_MEMBER_PARTIAL-DAG: Signature: add(_ self: Adder) -> (inout Int) -> () + // CURRY_MEMBER_PARTIAL-DAG: Signature: add(_ self: Adder) -> (AdditiveArithmetic, AdditiveArithmetic) -> AdditiveArithmetic + // CURRY_MEMBER_PARTIAL-DAG: Signature: add(_ self: Adder) -> (Double?, Float, Int) -> Double + // CURRY_MEMBER_PARTIAL-DAG: Signature: add(_ self: Adder) -> (Double, Float, Int) -> Double + // CURRY_MEMBER_PARTIAL-DAG: Signature: add(_ self: Adder) -> (Double...) -> Double + // CURRY_MEMBER_PARTIAL-DAG: Signature: add(_ self: Adder) -> (Int, Int, (Int, Int) throws -> Int) throws -> Int? + // CURRY_MEMBER_PARTIAL-DAG: Signature: add(_ self: Adder) -> (Int) -> (Int) -> Int +} + +func testCurryMemberFull() { + let adder = Adder() + Adder.add(adder)(#^CURRY_MEMBER_FULL^#) + // CURRY_MEMBER_FULL: Begin signatures, 8 items + // CURRY_MEMBER_FULL-DAG: Signature[Active]: (_ x: Int, to: Int) -> Int + // CURRY_MEMBER_FULL-DAG: Signature: (oneTo: inout Int) + // CURRY_MEMBER_FULL-DAG: Signature: (_ x: AdditiveArithmetic, to: AdditiveArithmetic) -> AdditiveArithmetic + // CURRY_MEMBER_FULL-DAG: Signature: (first: Double!, second: Float, third: Int) -> Double + // CURRY_MEMBER_FULL-DAG: Signature: (arg1: Double, arg2: Float, arg3: Int) -> Double + // CURRY_MEMBER_FULL-DAG: Signature: (numbers: Double...) -> Double + // CURRY_MEMBER_FULL-DAG: Signature: (x: Int, y: Int, with: (Int, Int) throws -> Int) throws -> Int! + // CURRY_MEMBER_FULL-DAG: Signature: (x: Int) -> (Int) -> Int +} diff --git a/test/IDE/signature_help_default_args.swift b/test/IDE/signature_help_default_args.swift new file mode 100644 index 0000000000000..0e07b2db06e02 --- /dev/null +++ b/test/IDE/signature_help_default_args.swift @@ -0,0 +1,34 @@ +// RUN: %target-swift-ide-test -signature-help -code-completion-token=DEFAULT_ARGS -source-filename=%s | %FileCheck %s --check-prefix=DEFAULT_ARGS + +func add(_ x: Int = 10, to y: Int) -> Int {} + +func add(oneTo x: inout Int) {} + +func add(_ x: Int, to y: Int? = nil) -> String {} + +func add(first: Double!, second: Float = .pi, third: Int) -> Double {} + +struct S { + let a: Bool +} + +func add(s: S = S(a: false)) -> Double {} + +func add(x: Int, y: Int, with adder: (Int, Int) -> Int = { $0 + $1 }) -> Int {} + +let importantValue = 42 + +func add(x: Int = importantValue) {} + +func add(x: Int, line: UInt = #line, file: StaticString = #file) {} + +add(#^DEFAULT_ARGS^#) +// DEFAULT_ARGS: Begin signatures, 8 items +// DEFAULT_ARGS-DAG: Signature[Active]: add(_ x: Int = 10, to: Int) -> Int +// DEFAULT_ARGS-DAG: Signature: add(oneTo: inout Int) +// DEFAULT_ARGS-DAG: Signature: add(_ x: Int, to: Int? = nil) -> String +// DEFAULT_ARGS-DAG: Signature: add(first: Double!, second: Float = .pi, third: Int) -> Double +// DEFAULT_ARGS-DAG: Signature: add(s: S = S(a: false)) -> Double +// DEFAULT_ARGS-DAG: Signature: add(x: Int, y: Int, with: (Int, Int) -> Int = { $0 + $1 }) -> Int +// DEFAULT_ARGS-DAG: Signature: add(x: Int = importantValue) +// DEFAULT_ARGS-DAG: Signature: add(x: Int, line: UInt = #line, file: StaticString = #file) diff --git a/test/IDE/signature_help_doc.swift b/test/IDE/signature_help_doc.swift new file mode 100644 index 0000000000000..6c741b34051c5 --- /dev/null +++ b/test/IDE/signature_help_doc.swift @@ -0,0 +1,21 @@ +// RUN: %target-swift-ide-test -signature-help -code-completion-token=DOC -source-filename=%s | %FileCheck %s --check-prefix=DOC + +/// Adds two integers. +/// +/// - Parameters: +/// - x: The first integer to add. +/// - y: The second integer to add. +/// +/// Usage: +/// ```swift +/// add(1, to: 2) // 3 +/// ``` +/// +/// - Returns: The sum of the two integers. +func add(_ x: Int, to y: Int) -> Int { + return x + y +} + +add(#^DOC^#) +// DOC: Begin signatures, 1 items +// DOC-DAG: Signature[Active]: add(_ x: Int, to: Int) -> Int; Documentation=Adds two integers.\n\n- Parameters:\n - x: The first integer to add.\n - y: The second integer to add.\n\nUsage:\n```swift\nadd(1, to: 2) // 3\n```\n\n- Returns: The sum of the two integers. diff --git a/test/IDE/signature_help_enum_case.swift b/test/IDE/signature_help_enum_case.swift new file mode 100644 index 0000000000000..b315676f4f518 --- /dev/null +++ b/test/IDE/signature_help_enum_case.swift @@ -0,0 +1,19 @@ +// RUN: %target-swift-ide-test -signature-help -code-completion-token=LABELLED -source-filename=%s | %FileCheck %s --check-prefix=LABELLED +// RUN: %target-swift-ide-test -signature-help -code-completion-token=UNLABELLED -source-filename=%s | %FileCheck %s --check-prefix=UNLABELLED + +enum Barcode { + case upc(numberSystem: Int, manufacturer: Int, product: Int, check: Int) + case qrCode(String) +} + +func testLabeled() { + Barcode.upc(#^LABELLED^#, manufacturer: 85909, product: 51226, check: 3) + // LABELLED: Begin signatures, 1 items + // LABELLED-DAG: Signature[Active]: upc(numberSystem: Int, manufacturer: Int, product: Int, check: Int) -> Barcode +} + +func testUnlabled() { + Barcode.qrCode(#^UNLABELLED^#) + // UNLABELLED: Begin signatures, 1 items + // UNLABELLED-DAG: Signature[Active]: qrCode(String) -> Barcode +} diff --git a/test/IDE/signature_help_init.swift b/test/IDE/signature_help_init.swift new file mode 100644 index 0000000000000..dd80e94b2cd28 --- /dev/null +++ b/test/IDE/signature_help_init.swift @@ -0,0 +1,9 @@ +// RUN: %target-swift-ide-test -signature-help -code-completion-token=INIT -source-filename=%s | %FileCheck %s --check-prefix=INIT + +struct Person { + init(name: String, age: Int, profession job: String) { } +} + +Person(name: "John", age: #^INIT^#) +// INIT: Begin signatures, 1 items +// INIT-DAG: Signature[Active]: init(name: String, age: Int, profession: String) diff --git a/test/IDE/signature_help_member_closure.swift b/test/IDE/signature_help_member_closure.swift new file mode 100644 index 0000000000000..8e175a29f5066 --- /dev/null +++ b/test/IDE/signature_help_member_closure.swift @@ -0,0 +1,11 @@ +// RUN: %target-swift-ide-test -signature-help -code-completion-token=MEMBER_CLOSURE -source-filename=%s | %FileCheck %s --check-prefix=MEMBER_CLOSURE + +struct Observable { + var observer: (String, Int?, [AnyHashable: [Double?]]) async throws -> [Observable?] + + func notify() async throws { + _ = try await observer("EVENT", #^MEMBER_CLOSURE^#, [:]) + // MEMBER_CLOSURE: Begin signatures, 1 items + // MEMBER_CLOSURE-DAG: Signature[Active]: observer(String, Int?, [AnyHashable : [Double?]]) async throws -> [Observable?] + } +} diff --git a/test/IDE/signature_help_member_func.swift b/test/IDE/signature_help_member_func.swift new file mode 100644 index 0000000000000..46cd62dedb8ee --- /dev/null +++ b/test/IDE/signature_help_member_func.swift @@ -0,0 +1,47 @@ +// RUN: %target-swift-ide-test -signature-help -code-completion-token=MEMBER_FUNC -source-filename=%s | %FileCheck %s --check-prefix=MEMBER_FUNC + +struct Adder { + func add(_ x: Int, to y: Int) -> Int { + return x + y + } + + func add(oneTo x: inout Int) { + x += 1 + } + + func add(_ x: T, to y: T) -> T { + return x + y + } + + func add(first: Double!, second: Float, third: Int) -> Double { + return first + Double(second) + Double(third) + } + + func add(arg1 param1: Double, arg2: Float, arg3 param3: Int) -> Double { + return param1 + Double(arg2) + Double(param3) + } + + func add(numbers: Double...) -> Double { + return numbers.reduce(into: 0) { $0 += $1 } + } + + func add(x: Int, y: Int, with adder: (Int, Int) -> Int) -> Int { + return adder(x, y) + } + + func add(x: Int) -> (Int) -> Int { + return { (y: Int) in x + y } + } +} + +let adder = Adder() +adder.add(#^MEMBER_FUNC^#) +// MEMBER_FUNC: Begin signatures, 8 items +// MEMBER_FUNC-DAG: Signature[Active]: add(_ x: Int, to: Int) -> Int +// MEMBER_FUNC-DAG: Signature: add(oneTo: inout Int) +// MEMBER_FUNC-DAG: Signature: add(_ x: AdditiveArithmetic, to: AdditiveArithmetic) -> AdditiveArithmetic +// MEMBER_FUNC-DAG: Signature: add(first: Double!, second: Float, third: Int) -> Double +// MEMBER_FUNC-DAG: Signature: add(arg1: Double, arg2: Float, arg3: Int) -> Double +// MEMBER_FUNC-DAG: Signature: add(numbers: Double...) -> Double +// MEMBER_FUNC-DAG: Signature: add(x: Int, y: Int, with: (Int, Int) -> Int) -> Int +// MEMBER_FUNC-DAG: Signature: add(x: Int) -> (Int) -> Int diff --git a/test/IDE/signature_help_member_generic.swift b/test/IDE/signature_help_member_generic.swift new file mode 100644 index 0000000000000..291dfa59a4c9e --- /dev/null +++ b/test/IDE/signature_help_member_generic.swift @@ -0,0 +1,12 @@ +// RUN: %target-swift-ide-test -signature-help -code-completion-token=MEMBER_GENERIC -source-filename=%s | %FileCheck %s --check-prefix=MEMBER_GENERIC + +struct Vector { + init(elements: [Value]) { } + + func dot(with other: Vector) -> Value { fatalError() } +} + +let vec = Vector(elements: [1.0, 2.1, 3.4]) +vec.dot(with: #^MEMBER_GENERIC^#) +// MEMBER_GENERIC: Begin signatures, 1 items +// MEMBER_GENERIC-DAG: Signature[Active]: dot(with: Vector) -> Double diff --git a/test/IDE/signature_help_member_subscript.swift b/test/IDE/signature_help_member_subscript.swift new file mode 100644 index 0000000000000..3dca0ddb1dc03 --- /dev/null +++ b/test/IDE/signature_help_member_subscript.swift @@ -0,0 +1,23 @@ +// RUN: %target-swift-ide-test -signature-help -code-completion-token=MEMBER_SUBSCRIPT -source-filename=%s | %FileCheck %s --check-prefix=MEMBER_SUBSCRIPT + +struct Matrix { + subscript(row: Int, column: Int) -> Int { + return 0 + } + + subscript(row r: Int) -> [Int] { + return [] + } + + subscript(column c: Int) -> [Int] { + return [] + } +} + +let matrix = Matrix() +matrix[#^MEMBER_SUBSCRIPT^#] +// MEMBER_SUBSCRIPT: Begin signatures, 4 items +// MEMBER_SUBSCRIPT-DAG: Signature[Active]: subscript(keyPath: KeyPath) -> Value +// MEMBER_SUBSCRIPT-DAG: Signature: subscript(row: Int, column: Int) -> Int +// MEMBER_SUBSCRIPT-DAG: Signature: subscript(row: Int) -> [Int] +// MEMBER_SUBSCRIPT-DAG: Signature: subscript(column: Int) -> [Int] diff --git a/test/IDE/signature_help_raw_identifier.swift b/test/IDE/signature_help_raw_identifier.swift new file mode 100644 index 0000000000000..72c00378a95f3 --- /dev/null +++ b/test/IDE/signature_help_raw_identifier.swift @@ -0,0 +1,9 @@ +// RUN: %target-swift-ide-test -signature-help -code-completion-token=RAW_ID -source-filename=%s | %FileCheck %s --check-prefix=RAW_ID + +struct `Raw Identifier` { + func `some method :)`(`argument label!` `param label?`: Int) {} +} + +`Raw Identifier`().`some method :)`(#^RAW_ID^#) +// RAW_ID: Begin signatures, 1 items +// RAW_ID-DAG: Signature[Active]: `some method :)`(`argument label!`: Int) diff --git a/test/IDE/signature_help_top_level.swift b/test/IDE/signature_help_top_level.swift new file mode 100644 index 0000000000000..f5e0bdb787c8d --- /dev/null +++ b/test/IDE/signature_help_top_level.swift @@ -0,0 +1,44 @@ +// RUN: %target-swift-ide-test -signature-help -code-completion-token=TOP_LEVEL -source-filename=%s | %FileCheck %s --check-prefix=TOP_LEVEL + +func add(_ x: Int, to y: Int) -> Int { + return x + y +} + +func add(oneTo x: inout Int) { + x += 1 +} + +func add(_ x: T, to y: T) -> T { + return x + y +} + +func add(first: Double!, second: Float, third: Int) -> Double { + return first + Double(second) + Double(third) +} + +func add(arg1 param1: Double, arg2: Float, arg3 param3: Int) -> Double { + return param1 + Double(arg2) + Double(param3) +} + +func add(numbers: Double...) -> Double { + return numbers.reduce(into: 0) { $0 += $1 } +} + +func add(x: Int, y: Int, with adder: (Int, Int) -> Int) -> Int { + return adder(x, y) +} + +func add(x: Int) -> (Int) -> Int { + return { (y: Int) in x + y } +} + +add(#^TOP_LEVEL^#) +// TOP_LEVEL: Begin signatures, 8 items +// TOP_LEVEL-DAG: Signature[Active]: add(_ x: Int, to: Int) -> Int +// TOP_LEVEL-DAG: Signature: add(oneTo: inout Int) +// TOP_LEVEL-DAG: Signature: add(_ x: AdditiveArithmetic, to: AdditiveArithmetic) -> AdditiveArithmetic +// TOP_LEVEL-DAG: Signature: add(first: Double!, second: Float, third: Int) -> Double +// TOP_LEVEL-DAG: Signature: add(arg1: Double, arg2: Float, arg3: Int) -> Double +// TOP_LEVEL-DAG: Signature: add(numbers: Double...) -> Double +// TOP_LEVEL-DAG: Signature: add(x: Int, y: Int, with: (Int, Int) -> Int) -> Int +// TOP_LEVEL-DAG: Signature: add(x: Int) -> (Int) -> Int diff --git a/test/IDE/signature_help_top_level_generic.swift b/test/IDE/signature_help_top_level_generic.swift new file mode 100644 index 0000000000000..af9640d898d3f --- /dev/null +++ b/test/IDE/signature_help_top_level_generic.swift @@ -0,0 +1,9 @@ +// RUN: %target-swift-ide-test -signature-help -code-completion-token=TOP_LEVEL_GENERIC -source-filename=%s | %FileCheck %s --check-prefix=TOP_LEVEL_GENERIC + +func add(x: T, y: T, with adder: (T, T) -> T) -> T where T: AdditiveArithmetic { + return adder(x, y) +} + +add(x: "A", y: "B", with: #^TOP_LEVEL_GENERIC^#) +// TOP_LEVEL_GENERIC: Begin signatures, 1 items +// TOP_LEVEL_GENERIC-DAG: Signature[Active]: add(x: String, y: String, with: (String, String) -> String) -> String diff --git a/test/SourceKit/SignatureHelp/signature_help.swift b/test/SourceKit/SignatureHelp/signature_help.swift new file mode 100644 index 0000000000000..e6bc51c1fb0a7 --- /dev/null +++ b/test/SourceKit/SignatureHelp/signature_help.swift @@ -0,0 +1,64 @@ +// RUN: %empty-directory(%t) +// RUN: split-file %s %t +// RUN: %sourcekitd-test -req=signaturehelp -pos=18:10 %t/input.swift -- %t/input.swift > %t/actual.result +// RUN: diff -u %t/expected.result %t/actual.result + +//--- input.swift +struct Calculator { + /// Adds two numbers together. + /// + /// - Parameters: + /// - x: The first number to add. + /// - y: The second number to add. + /// - Returns: The sum of x and y. + func add(_ x: Int, to y: Int) -> Int { + return x + y + } + + func add(first: Double, second: Float) -> Double { + return first + Double(second) + } +} + +let calc = Calculator() +calc.add() + +//--- expected.result +{ + key.signatures: [ + { + key.name: "add(_ x: Int, to: Int) -> Int", + key.doc_comment: "Adds two numbers together.\n\n- Parameters:\n - x: The first number to add.\n - y: The second number to add.\n- Returns: The sum of x and y.", + key.parameters: [ + { + key.name: "x", + key.nameoffset: 4, + key.namelength: 8 + }, + { + key.name: "y", + key.nameoffset: 14, + key.namelength: 7 + } + ], + key.active_parameter: 0 + }, + { + key.name: "add(first: Double, second: Float) -> Double", + key.parameters: [ + { + key.name: "first", + key.nameoffset: 4, + key.namelength: 13 + }, + { + key.name: "second", + key.nameoffset: 19, + key.namelength: 13 + } + ], + key.active_parameter: 0 + } + ], + key.active_signature: 0 +} diff --git a/test/SourceKit/SignatureHelp/signature_help_closure_param.swift b/test/SourceKit/SignatureHelp/signature_help_closure_param.swift deleted file mode 100644 index f2edff368c4b0..0000000000000 --- a/test/SourceKit/SignatureHelp/signature_help_closure_param.swift +++ /dev/null @@ -1,26 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: split-file %s %t -// RUN: %sourcekitd-test -req=signaturehelp -pos=2:15 %t/input.swift -- %t/input.swift > %t/actual.result -// RUN: diff -u %t/expected.result %t/actual.result - -//--- input.swift -func apply(value: Value, body: (Value) -> Result) -> Result { - return body() -} - -//--- expected.result -{ - key.signatures: [ - { - key.name: "body(Value) -> Result", - key.parameters: [ - { - key.nameoffset: 5, - key.namelength: 5 - } - ], - key.active_parameter: 0 - } - ], - key.active_signature: 0 -} diff --git a/test/SourceKit/SignatureHelp/signature_help_currying.swift b/test/SourceKit/SignatureHelp/signature_help_currying.swift deleted file mode 100644 index cb1de9c00d238..0000000000000 --- a/test/SourceKit/SignatureHelp/signature_help_currying.swift +++ /dev/null @@ -1,305 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: split-file %s %t -// RUN: %sourcekitd-test -req=signaturehelp -pos=40:25 %t/input.swift -- %t/input.swift > %t/actual_curry_toplevel.result -// RUN: diff -u %t/expected_curry_toplevel.result %t/actual_curry_toplevel.result -// RUN: %sourcekitd-test -req=signaturehelp -pos=44:13 %t/input.swift -- %t/input.swift > %t/actual_curry_member_partial.result -// RUN: diff -u %t/expected_curry_member_partial.result %t/actual_curry_member_partial.result -// RUN: %sourcekitd-test -req=signaturehelp -pos=49:20 %t/input.swift -- %t/input.swift > %t/actual_curry_member_full.result -// RUN: diff -u %t/expected_curry_member_full.result %t/actual_curry_member_full.result - -//--- input.swift -struct Adder { - func add(_ x: Int, to y: Int) -> Int { - return x + y - } - - func add(oneTo x: inout Int) { - x += 1 - } - - func add(_ x: T, to y: T) -> T { - return x + y - } - - func add(first: Double!, second: Float, third: Int) -> Double { - return first + Double(second) + Double(third) - } - - func add(arg1 param1: Double, arg2: Float, arg3 param3: Int) -> Double { - return param1 + Double(arg2) + Double(param3) - } - - func add(numbers: Double...) -> Double { - return numbers.reduce(into: 0) { $0 += $1 } - } - - func add(x: Int, y: Int, with adder: (Int, Int) throws -> Int) rethrows -> Int! { - return adder(x, y) - } - - func add(x: Int) -> (Int) -> Int { - return { (y: Int) in x + y } - } -} - -func topLevelCurried(x: Int) -> (Double) -> (String) -> Void { - fatalError() -} - -func testCurryTopLevel() { - topLevelCurried(x: 1)() -} - -func testCurryMemberPartial() { - Adder.add() -} - -func testCurryMemberFull() { - let adder = Adder() - Adder.add(adder)() -} - -//--- expected_curry_toplevel.result -{ - key.signatures: [ - { - key.name: "(Double) -> (String) -> Void", - key.parameters: [ - { - key.nameoffset: 1, - key.namelength: 6 - } - ], - key.active_parameter: 0 - } - ], - key.active_signature: 0 -} -//--- expected_curry_member_partial.result -{ - key.signatures: [ - { - key.name: "add(_ self: Adder) -> (Int, Int) -> Int", - key.parameters: [ - { - key.name: "self", - key.nameoffset: 4, - key.namelength: 13 - } - ], - key.active_parameter: 0 - }, - { - key.name: "add(_ self: Adder) -> (inout Int) -> ()", - key.parameters: [ - { - key.name: "self", - key.nameoffset: 4, - key.namelength: 13 - } - ], - key.active_parameter: 0 - }, - { - key.name: "add(_ self: Adder) -> (AdditiveArithmetic, AdditiveArithmetic) -> AdditiveArithmetic", - key.parameters: [ - { - key.name: "self", - key.nameoffset: 4, - key.namelength: 13 - } - ], - key.active_parameter: 0 - }, - { - key.name: "add(_ self: Adder) -> (Double?, Float, Int) -> Double", - key.parameters: [ - { - key.name: "self", - key.nameoffset: 4, - key.namelength: 13 - } - ], - key.active_parameter: 0 - }, - { - key.name: "add(_ self: Adder) -> (Double, Float, Int) -> Double", - key.parameters: [ - { - key.name: "self", - key.nameoffset: 4, - key.namelength: 13 - } - ], - key.active_parameter: 0 - }, - { - key.name: "add(_ self: Adder) -> (Double...) -> Double", - key.parameters: [ - { - key.name: "self", - key.nameoffset: 4, - key.namelength: 13 - } - ], - key.active_parameter: 0 - }, - { - key.name: "add(_ self: Adder) -> (Int, Int, (Int, Int) throws -> Int) throws -> Int?", - key.parameters: [ - { - key.name: "self", - key.nameoffset: 4, - key.namelength: 13 - } - ], - key.active_parameter: 0 - }, - { - key.name: "add(_ self: Adder) -> (Int) -> (Int) -> Int", - key.parameters: [ - { - key.name: "self", - key.nameoffset: 4, - key.namelength: 13 - } - ], - key.active_parameter: 0 - } - ], - key.active_signature: 0 -} -//--- expected_curry_member_full.result -{ - key.signatures: [ - { - key.name: "(_ x: Int, to: Int) -> Int", - key.parameters: [ - { - key.name: "x", - key.nameoffset: 1, - key.namelength: 8 - }, - { - key.name: "y", - key.nameoffset: 11, - key.namelength: 7 - } - ], - key.active_parameter: 0 - }, - { - key.name: "(oneTo: inout Int)", - key.parameters: [ - { - key.name: "x", - key.nameoffset: 1, - key.namelength: 16 - } - ], - key.active_parameter: 0 - }, - { - key.name: "(_ x: AdditiveArithmetic, to: AdditiveArithmetic) -> AdditiveArithmetic", - key.parameters: [ - { - key.name: "x", - key.nameoffset: 1, - key.namelength: 23 - }, - { - key.name: "y", - key.nameoffset: 26, - key.namelength: 22 - } - ], - key.active_parameter: 0 - }, - { - key.name: "(first: Double!, second: Float, third: Int) -> Double", - key.parameters: [ - { - key.name: "first", - key.nameoffset: 1, - key.namelength: 14 - }, - { - key.name: "second", - key.nameoffset: 17, - key.namelength: 13 - }, - { - key.name: "third", - key.nameoffset: 32, - key.namelength: 10 - } - ], - key.active_parameter: 0 - }, - { - key.name: "(arg1: Double, arg2: Float, arg3: Int) -> Double", - key.parameters: [ - { - key.name: "param1", - key.nameoffset: 1, - key.namelength: 12 - }, - { - key.name: "arg2", - key.nameoffset: 15, - key.namelength: 11 - }, - { - key.name: "param3", - key.nameoffset: 28, - key.namelength: 9 - } - ], - key.active_parameter: 0 - }, - { - key.name: "(numbers: Double...) -> Double", - key.parameters: [ - { - key.name: "numbers", - key.nameoffset: 1, - key.namelength: 18 - } - ], - key.active_parameter: 0 - }, - { - key.name: "(x: Int, y: Int, with: (Int, Int) throws -> Int) throws -> Int!", - key.parameters: [ - { - key.name: "x", - key.nameoffset: 1, - key.namelength: 6 - }, - { - key.name: "y", - key.nameoffset: 9, - key.namelength: 6 - }, - { - key.name: "adder", - key.nameoffset: 17, - key.namelength: 30 - } - ], - key.active_parameter: 0 - }, - { - key.name: "(x: Int) -> (Int) -> Int", - key.parameters: [ - { - key.name: "x", - key.nameoffset: 1, - key.namelength: 6 - } - ], - key.active_parameter: 0 - } - ], - key.active_signature: 0 -} diff --git a/test/SourceKit/SignatureHelp/signature_help_default_args.swift b/test/SourceKit/SignatureHelp/signature_help_default_args.swift deleted file mode 100644 index 057fc4180f83f..0000000000000 --- a/test/SourceKit/SignatureHelp/signature_help_default_args.swift +++ /dev/null @@ -1,164 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: split-file %s %t -// RUN: %sourcekitd-test -req=signaturehelp -pos=23:5 %t/input.swift -- %t/input.swift > %t/actual.result -// RUN: diff -u %t/expected.result %t/actual.result - -//--- input.swift -func add(_ x: Int = 10, to y: Int) -> Int {} - -func add(oneTo x: inout Int) {} - -func add(_ x: Int, to y: Int? = nil) -> String {} - -func add(first: Double!, second: Float = .pi, third: Int) -> Double {} - -struct S { - let a: Bool -} - -func add(s: S = S(a: false)) -> Double {} - -func add(x: Int, y: Int, with adder: (Int, Int) -> Int = { $0 + $1 }) -> Int {} - -let importantValue = 42 - -func add(x: Int = importantValue) {} - -func add(x: Int, line: UInt = #line, file: StaticString = #file) {} - -add() - -//--- expected.result -{ - key.signatures: [ - { - key.name: "add(_ x: Int = 10, to: Int) -> Int", - key.parameters: [ - { - key.name: "x", - key.nameoffset: 4, - key.namelength: 13 - }, - { - key.name: "y", - key.nameoffset: 19, - key.namelength: 7 - } - ], - key.active_parameter: 0 - }, - { - key.name: "add(oneTo: inout Int)", - key.parameters: [ - { - key.name: "x", - key.nameoffset: 4, - key.namelength: 16 - } - ], - key.active_parameter: 0 - }, - { - key.name: "add(_ x: Int, to: Int? = nil) -> String", - key.parameters: [ - { - key.name: "x", - key.nameoffset: 4, - key.namelength: 8 - }, - { - key.name: "y", - key.nameoffset: 14, - key.namelength: 14 - } - ], - key.active_parameter: 0 - }, - { - key.name: "add(first: Double!, second: Float = .pi, third: Int) -> Double", - key.parameters: [ - { - key.name: "first", - key.nameoffset: 4, - key.namelength: 14 - }, - { - key.name: "second", - key.nameoffset: 20, - key.namelength: 19 - }, - { - key.name: "third", - key.nameoffset: 41, - key.namelength: 10 - } - ], - key.active_parameter: 0 - }, - { - key.name: "add(s: S = S(a: false)) -> Double", - key.parameters: [ - { - key.name: "s", - key.nameoffset: 4, - key.namelength: 18 - } - ], - key.active_parameter: 0 - }, - { - key.name: "add(x: Int, y: Int, with: (Int, Int) -> Int = { $0 + $1 }) -> Int", - key.parameters: [ - { - key.name: "x", - key.nameoffset: 4, - key.namelength: 6 - }, - { - key.name: "y", - key.nameoffset: 12, - key.namelength: 6 - }, - { - key.name: "adder", - key.nameoffset: 20, - key.namelength: 37 - } - ], - key.active_parameter: 0 - }, - { - key.name: "add(x: Int = importantValue)", - key.parameters: [ - { - key.name: "x", - key.nameoffset: 4, - key.namelength: 23 - } - ], - key.active_parameter: 0 - }, - { - key.name: "add(x: Int, line: UInt = #line, file: StaticString = #file)", - key.parameters: [ - { - key.name: "x", - key.nameoffset: 4, - key.namelength: 6 - }, - { - key.name: "line", - key.nameoffset: 12, - key.namelength: 18 - }, - { - key.name: "file", - key.nameoffset: 32, - key.namelength: 26 - } - ], - key.active_parameter: 0 - } - ], - key.active_signature: 0 -} diff --git a/test/SourceKit/SignatureHelp/signature_help_doc.swift b/test/SourceKit/SignatureHelp/signature_help_doc.swift deleted file mode 100644 index 171a226539d2f..0000000000000 --- a/test/SourceKit/SignatureHelp/signature_help_doc.swift +++ /dev/null @@ -1,47 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: split-file %s %t -// RUN: %sourcekitd-test -req=signaturehelp -pos=17:8 %t/input.swift -- %t/input.swift > %t/actual.result -// RUN: diff -u %t/expected.result %t/actual.result - -//--- input.swift -/// Adds two integers. -/// -/// - Parameters: -/// - x: The first integer to add. -/// - y: The second integer to add. -/// -/// Usage: -/// ```swift -/// add(1, to: 2) // 3 -/// ``` -/// -/// - Returns: The sum of the two integers. -func add(_ x: Int, to y: Int) -> Int { - return x + y -} - -add(x: ) - -//--- expected.result -{ - key.signatures: [ - { - key.name: "add(_ x: Int, to: Int) -> Int", - key.doc_comment: "Adds two integers.\n\n- Parameters:\n - x: The first integer to add.\n - y: The second integer to add.\n\nUsage:\n```swift\nadd(1, to: 2) // 3\n```\n\n- Returns: The sum of the two integers.", - key.parameters: [ - { - key.name: "x", - key.nameoffset: 4, - key.namelength: 8 - }, - { - key.name: "y", - key.nameoffset: 14, - key.namelength: 7 - } - ], - key.active_parameter: 0 - } - ], - key.active_signature: 0 -} diff --git a/test/SourceKit/SignatureHelp/signature_help_enum_case.swift b/test/SourceKit/SignatureHelp/signature_help_enum_case.swift deleted file mode 100644 index b1edb3e5cd60c..0000000000000 --- a/test/SourceKit/SignatureHelp/signature_help_enum_case.swift +++ /dev/null @@ -1,69 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: split-file %s %t -// RUN: %sourcekitd-test -req=signaturehelp -pos=7:15 %t/input.swift -- %t/input.swift > %t/actual_labelled.result -// RUN: diff -u %t/expected_labelled.result %t/actual_labelled.result -// RUN: %sourcekitd-test -req=signaturehelp -pos=11:18 %t/input.swift -- %t/input.swift > %t/actual_unlabelled.result -// RUN: diff -u %t/expected_unlabelled.result %t/actual_unlabelled.result - -//--- input.swift -enum Barcode { - case upc(numberSystem: Int, manufacturer: Int, product: Int, check: Int) - case qrCode(String) -} - -func testLabeled() { - Barcode.upc(, manufacturer: 85909, product: 51226, check: 3) -} - -func testUnlabled() { - Barcode.qrCode() -} - -//--- expected_labelled.result -{ - key.signatures: [ - { - key.name: "upc(numberSystem: Int, manufacturer: Int, product: Int, check: Int) -> Barcode", - key.parameters: [ - { - key.name: "numberSystem", - key.nameoffset: 4, - key.namelength: 17 - }, - { - key.name: "manufacturer", - key.nameoffset: 23, - key.namelength: 17 - }, - { - key.name: "product", - key.nameoffset: 42, - key.namelength: 12 - }, - { - key.name: "check", - key.nameoffset: 56, - key.namelength: 10 - } - ], - key.active_parameter: 0 - } - ], - key.active_signature: 0 -} -//--- expected_unlabelled.result -{ - key.signatures: [ - { - key.name: "qrCode(String) -> Barcode", - key.parameters: [ - { - key.nameoffset: 7, - key.namelength: 6 - } - ], - key.active_parameter: 0 - } - ], - key.active_signature: 0 -} diff --git a/test/SourceKit/SignatureHelp/signature_help_init.swift b/test/SourceKit/SignatureHelp/signature_help_init.swift deleted file mode 100644 index 24528437173cb..0000000000000 --- a/test/SourceKit/SignatureHelp/signature_help_init.swift +++ /dev/null @@ -1,39 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: split-file %s %t -// RUN: %sourcekitd-test -req=signaturehelp -pos=5:27 %t/input.swift -- %t/input.swift > %t/actual.result -// RUN: diff -u %t/expected.result %t/actual.result - -//--- input.swift -struct Person { - init(name: String, age: Int, profession job: String) { } -} - -Person(name: "John", age: ) - -//--- expected.result -{ - key.signatures: [ - { - key.name: "init(name: String, age: Int, profession: String)", - key.parameters: [ - { - key.name: "name", - key.nameoffset: 5, - key.namelength: 12 - }, - { - key.name: "age", - key.nameoffset: 19, - key.namelength: 8 - }, - { - key.name: "job", - key.nameoffset: 29, - key.namelength: 18 - } - ], - key.active_parameter: 1 - } - ], - key.active_signature: 0 -} diff --git a/test/SourceKit/SignatureHelp/signature_help_member_closure.swift b/test/SourceKit/SignatureHelp/signature_help_member_closure.swift deleted file mode 100644 index e71d7afc30c31..0000000000000 --- a/test/SourceKit/SignatureHelp/signature_help_member_closure.swift +++ /dev/null @@ -1,38 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: split-file %s %t -// RUN: %sourcekitd-test -req=signaturehelp -pos=5:23 %t/input.swift -- %t/input.swift > %t/actual.result -// RUN: diff -u %t/expected.result %t/actual.result - -//--- input.swift -struct Observable { - var observer: (String, Int?, [AnyHashable: [Double?]]) async throws -> [Observable?] - - func notify() async throws { - observer("EVENT", , [:]) - } -} - -//--- expected.result -{ - key.signatures: [ - { - key.name: "observer(String, Int?, [AnyHashable : [Double?]]) async throws -> [Observable?]", - key.parameters: [ - { - key.nameoffset: 9, - key.namelength: 6 - }, - { - key.nameoffset: 17, - key.namelength: 4 - }, - { - key.nameoffset: 23, - key.namelength: 25 - } - ], - key.active_parameter: 1 - } - ], - key.active_signature: 0 -} diff --git a/test/SourceKit/SignatureHelp/signature_help_member_func.swift b/test/SourceKit/SignatureHelp/signature_help_member_func.swift deleted file mode 100644 index c1a4f921fe61f..0000000000000 --- a/test/SourceKit/SignatureHelp/signature_help_member_func.swift +++ /dev/null @@ -1,177 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: split-file %s %t -// RUN: %sourcekitd-test -req=signaturehelp -pos=36:11 %t/input.swift -- %t/input.swift > %t/actual.result -// RUN: diff -u %t/expected.result %t/actual.result - -//--- input.swift -struct Adder { - func add(_ x: Int, to y: Int) -> Int { - return x + y - } - - func add(oneTo x: inout Int) { - x += 1 - } - - func add(_ x: T, to y: T) -> T { - return x + y - } - - func add(first: Double!, second: Float, third: Int) -> Double { - return first + Double(second) + Double(third) - } - - func add(arg1 param1: Double, arg2: Float, arg3 param3: Int) -> Double { - return param1 + Double(arg2) + Double(param3) - } - - func add(numbers: Double...) -> Double { - return numbers.reduce(into: 0) { $0 += $1 } - } - - func add(x: Int, y: Int, with adder: (Int, Int) -> Int) -> Int { - return adder(x, y) - } - - func add(x: Int) -> (Int) -> Int { - return { (y: Int) in x + y } - } -} - -let adder = Adder() -adder.add() - -//--- expected.result -{ - key.signatures: [ - { - key.name: "add(_ x: Int, to: Int) -> Int", - key.parameters: [ - { - key.name: "x", - key.nameoffset: 4, - key.namelength: 8 - }, - { - key.name: "y", - key.nameoffset: 14, - key.namelength: 7 - } - ], - key.active_parameter: 0 - }, - { - key.name: "add(oneTo: inout Int)", - key.parameters: [ - { - key.name: "x", - key.nameoffset: 4, - key.namelength: 16 - } - ], - key.active_parameter: 0 - }, - { - key.name: "add(_ x: AdditiveArithmetic, to: AdditiveArithmetic) -> AdditiveArithmetic", - key.parameters: [ - { - key.name: "x", - key.nameoffset: 4, - key.namelength: 23 - }, - { - key.name: "y", - key.nameoffset: 29, - key.namelength: 22 - } - ], - key.active_parameter: 0 - }, - { - key.name: "add(first: Double!, second: Float, third: Int) -> Double", - key.parameters: [ - { - key.name: "first", - key.nameoffset: 4, - key.namelength: 14 - }, - { - key.name: "second", - key.nameoffset: 20, - key.namelength: 13 - }, - { - key.name: "third", - key.nameoffset: 35, - key.namelength: 10 - } - ], - key.active_parameter: 0 - }, - { - key.name: "add(arg1: Double, arg2: Float, arg3: Int) -> Double", - key.parameters: [ - { - key.name: "param1", - key.nameoffset: 4, - key.namelength: 12 - }, - { - key.name: "arg2", - key.nameoffset: 18, - key.namelength: 11 - }, - { - key.name: "param3", - key.nameoffset: 31, - key.namelength: 9 - } - ], - key.active_parameter: 0 - }, - { - key.name: "add(numbers: Double...) -> Double", - key.parameters: [ - { - key.name: "numbers", - key.nameoffset: 4, - key.namelength: 18 - } - ], - key.active_parameter: 0 - }, - { - key.name: "add(x: Int, y: Int, with: (Int, Int) -> Int) -> Int", - key.parameters: [ - { - key.name: "x", - key.nameoffset: 4, - key.namelength: 6 - }, - { - key.name: "y", - key.nameoffset: 12, - key.namelength: 6 - }, - { - key.name: "adder", - key.nameoffset: 20, - key.namelength: 23 - } - ], - key.active_parameter: 0 - }, - { - key.name: "add(x: Int) -> (Int) -> Int", - key.parameters: [ - { - key.name: "x", - key.nameoffset: 4, - key.namelength: 6 - } - ], - key.active_parameter: 0 - } - ], - key.active_signature: 0 -} diff --git a/test/SourceKit/SignatureHelp/signature_help_member_generic.swift b/test/SourceKit/SignatureHelp/signature_help_member_generic.swift deleted file mode 100644 index 732703439a6b1..0000000000000 --- a/test/SourceKit/SignatureHelp/signature_help_member_generic.swift +++ /dev/null @@ -1,32 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: split-file %s %t -// RUN: %sourcekitd-test -req=signaturehelp -pos=8:15 %t/input.swift -- %t/input.swift > %t/actual.result -// RUN: diff -u %t/expected.result %t/actual.result - -//--- input.swift -struct Vector { - init(elements: [Value]) { } - - func dot(with other: Vector) -> Value { } -} - -let vec = Vector(elements: [1.0, 2.1, 3.4]) -vec.dot(with: ) - -//--- expected.result -{ - key.signatures: [ - { - key.name: "dot(with: Vector) -> Double", - key.parameters: [ - { - key.name: "other", - key.nameoffset: 4, - key.namelength: 20 - } - ], - key.active_parameter: 0 - } - ], - key.active_signature: 0 -} diff --git a/test/SourceKit/SignatureHelp/signature_help_member_subscript.swift b/test/SourceKit/SignatureHelp/signature_help_member_subscript.swift deleted file mode 100644 index 20ce499672d61..0000000000000 --- a/test/SourceKit/SignatureHelp/signature_help_member_subscript.swift +++ /dev/null @@ -1,77 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: split-file %s %t -// RUN: %sourcekitd-test -req=signaturehelp -pos=16:8 %t/input.swift -- %t/input.swift > %t/actual.result -// RUN: diff -u %t/expected.result %t/actual.result - -//--- input.swift -struct Matrix { - subscript(row: Int, column: Int) -> Int { - return 0 - } - - subscript(row r: Int) -> [Int] { - return [] - } - - subscript(column c: Int) -> [Int] { - return [] - } -} - -let matrix = Matrix() -matrix[] - -//--- expected.result -{ - key.signatures: [ - { - key.name: "subscript(keyPath: KeyPath) -> Value", - key.parameters: [ - { - key.nameoffset: 10, - key.namelength: 31 - } - ], - key.active_parameter: 0 - }, - { - key.name: "subscript(row: Int, column: Int) -> Int", - key.parameters: [ - { - key.name: "row", - key.nameoffset: 10, - key.namelength: 8 - }, - { - key.name: "column", - key.nameoffset: 20, - key.namelength: 11 - } - ], - key.active_parameter: 0 - }, - { - key.name: "subscript(row: Int) -> [Int]", - key.parameters: [ - { - key.name: "r", - key.nameoffset: 10, - key.namelength: 8 - } - ], - key.active_parameter: 0 - }, - { - key.name: "subscript(column: Int) -> [Int]", - key.parameters: [ - { - key.name: "c", - key.nameoffset: 10, - key.namelength: 11 - } - ], - key.active_parameter: 0 - } - ], - key.active_signature: 0 -} diff --git a/test/SourceKit/SignatureHelp/signature_help_raw_identifier.swift b/test/SourceKit/SignatureHelp/signature_help_raw_identifier.swift deleted file mode 100644 index 7b3e40fa7fcb1..0000000000000 --- a/test/SourceKit/SignatureHelp/signature_help_raw_identifier.swift +++ /dev/null @@ -1,29 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: split-file %s %t -// RUN: %sourcekitd-test -req=signaturehelp -pos=5:37 %t/input.swift -- %t/input.swift > %t/actual.result -// RUN: diff -u %t/expected.result %t/actual.result - -//--- input.swift -struct `Raw Identifier` { - func `some method :)`(`argument label!` `param label?`: Int) {} -} - -`Raw Identifier`().`some method :)`() - -//--- expected.result -{ - key.signatures: [ - { - key.name: "`some method :)`(`argument label!`: Int)", - key.parameters: [ - { - key.name: "param label?", - key.nameoffset: 17, - key.namelength: 22 - } - ], - key.active_parameter: 0 - } - ], - key.active_signature: 0 -} diff --git a/test/SourceKit/SignatureHelp/signature_help_top_level.swift b/test/SourceKit/SignatureHelp/signature_help_top_level.swift deleted file mode 100644 index 0aa5f2a89372a..0000000000000 --- a/test/SourceKit/SignatureHelp/signature_help_top_level.swift +++ /dev/null @@ -1,174 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: split-file %s %t -// RUN: %sourcekitd-test -req=signaturehelp -pos=33:5 %t/input.swift -- %t/input.swift > %t/actual.result -// RUN: diff -u %t/expected.result %t/actual.result - -//--- input.swift -func add(_ x: Int, to y: Int) -> Int { - return x + y -} - -func add(oneTo x: inout Int) { - x += 1 -} - -func add(_ x: T, to y: T) -> T { - return x + y -} - -func add(first: Double!, second: Float, third: Int) -> Double { - return first + Double(second) + Double(third) -} - -func add(arg1 param1: Double, arg2: Float, arg3 param3: Int) -> Double { - return param1 + Double(arg2) + Double(param3) -} - -func add(numbers: Double...) -> Double { - return numbers.reduce(into: 0) { $0 += $1 } -} - -func add(x: Int, y: Int, with adder: (Int, Int) -> Int) -> Int { - return adder(x, y) -} - -func add(x: Int) -> (Int) -> Int { - return { (y: Int) in x + y } -} - -add() - -//--- expected.result -{ - key.signatures: [ - { - key.name: "add(_ x: Int, to: Int) -> Int", - key.parameters: [ - { - key.name: "x", - key.nameoffset: 4, - key.namelength: 8 - }, - { - key.name: "y", - key.nameoffset: 14, - key.namelength: 7 - } - ], - key.active_parameter: 0 - }, - { - key.name: "add(oneTo: inout Int)", - key.parameters: [ - { - key.name: "x", - key.nameoffset: 4, - key.namelength: 16 - } - ], - key.active_parameter: 0 - }, - { - key.name: "add(_ x: AdditiveArithmetic, to: AdditiveArithmetic) -> AdditiveArithmetic", - key.parameters: [ - { - key.name: "x", - key.nameoffset: 4, - key.namelength: 23 - }, - { - key.name: "y", - key.nameoffset: 29, - key.namelength: 22 - } - ], - key.active_parameter: 0 - }, - { - key.name: "add(first: Double!, second: Float, third: Int) -> Double", - key.parameters: [ - { - key.name: "first", - key.nameoffset: 4, - key.namelength: 14 - }, - { - key.name: "second", - key.nameoffset: 20, - key.namelength: 13 - }, - { - key.name: "third", - key.nameoffset: 35, - key.namelength: 10 - } - ], - key.active_parameter: 0 - }, - { - key.name: "add(arg1: Double, arg2: Float, arg3: Int) -> Double", - key.parameters: [ - { - key.name: "param1", - key.nameoffset: 4, - key.namelength: 12 - }, - { - key.name: "arg2", - key.nameoffset: 18, - key.namelength: 11 - }, - { - key.name: "param3", - key.nameoffset: 31, - key.namelength: 9 - } - ], - key.active_parameter: 0 - }, - { - key.name: "add(numbers: Double...) -> Double", - key.parameters: [ - { - key.name: "numbers", - key.nameoffset: 4, - key.namelength: 18 - } - ], - key.active_parameter: 0 - }, - { - key.name: "add(x: Int, y: Int, with: (Int, Int) -> Int) -> Int", - key.parameters: [ - { - key.name: "x", - key.nameoffset: 4, - key.namelength: 6 - }, - { - key.name: "y", - key.nameoffset: 12, - key.namelength: 6 - }, - { - key.name: "adder", - key.nameoffset: 20, - key.namelength: 23 - } - ], - key.active_parameter: 0 - }, - { - key.name: "add(x: Int) -> (Int) -> Int", - key.parameters: [ - { - key.name: "x", - key.nameoffset: 4, - key.namelength: 6 - } - ], - key.active_parameter: 0 - } - ], - key.active_signature: 0 -} diff --git a/test/SourceKit/SignatureHelp/signature_help_top_level_generic.swift b/test/SourceKit/SignatureHelp/signature_help_top_level_generic.swift deleted file mode 100644 index 2a3e58bda7fa2..0000000000000 --- a/test/SourceKit/SignatureHelp/signature_help_top_level_generic.swift +++ /dev/null @@ -1,39 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: split-file %s %t -// RUN: %sourcekitd-test -req=signaturehelp -pos=5:27 %t/input.swift -- %t/input.swift > %t/actual.result -// RUN: diff -u %t/expected.result %t/actual.result - -//--- input.swift -func add(x: T, y: T, with adder: (T, T) -> T) -> T where T: AdditiveArithmetic { - return adder(x, y) -} - -add(x: "A", y: "B", with: ) - -//--- expected.result -{ - key.signatures: [ - { - key.name: "add(x: String, y: String, with: (String, String) -> String) -> String", - key.parameters: [ - { - key.name: "x", - key.nameoffset: 4, - key.namelength: 9 - }, - { - key.name: "y", - key.nameoffset: 15, - key.namelength: 9 - }, - { - key.name: "adder", - key.nameoffset: 26, - key.namelength: 32 - } - ], - key.active_parameter: 2 - } - ], - key.active_signature: 0 -} diff --git a/tools/SourceKit/include/SourceKit/Core/LangSupport.h b/tools/SourceKit/include/SourceKit/Core/LangSupport.h index 9daf7a85afe8b..d60a7f6cd979d 100644 --- a/tools/SourceKit/include/SourceKit/Core/LangSupport.h +++ b/tools/SourceKit/include/SourceKit/Core/LangSupport.h @@ -35,6 +35,12 @@ namespace llvm { class MemoryBuffer; } +namespace swift { +namespace ide { +struct FormattedSignatureHelp; +} // namespace ide +} // namespace swift + namespace SourceKit { class GlobalConfig; using swift::ide::CancellableResult; @@ -1012,51 +1018,14 @@ class ConformingMethodListConsumer { virtual void cancelled() = 0; }; -struct SignatureHelpResponse { - struct Parameter { - /// The offset of the parameter text in the signature text. - unsigned Offset; - - /// The length of the parameter text in the signature text. - unsigned Length; - - /// The documentation comment for the parameter. - StringRef DocComment; - - /// The internal parameter name. - StringRef Name; - - Parameter() {} - }; - - struct Signature { - /// The text describing the signature. - StringRef Text; - - /// The documentation comment for the signature. - StringRef Doc; - - /// The index of the active parameter if any. - std::optional ActiveParam; - - /// The parameters for the signature. - ArrayRef Params; - }; - - /// The index of the active signature. - unsigned ActiveSignature; - - /// The available signatures/overloads. - ArrayRef Signatures; -}; - class SignatureHelpConsumer { virtual void anchor(); public: virtual ~SignatureHelpConsumer() {} - virtual void handleResult(const SignatureHelpResponse &Result) = 0; + virtual void + handleResult(const swift::ide::FormattedSignatureHelp &Result) = 0; virtual void setReusingASTContext(bool flag) = 0; virtual void failed(StringRef ErrDescription) = 0; virtual void cancelled() = 0; diff --git a/tools/SourceKit/lib/SwiftLang/SwiftSignatureHelp.cpp b/tools/SourceKit/lib/SwiftLang/SwiftSignatureHelp.cpp index 7ea8dc1852c1a..2b5ddb53a5169 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftSignatureHelp.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftSignatureHelp.cpp @@ -12,193 +12,14 @@ #include "SwiftASTManager.h" #include "SwiftLangSupport.h" -#include "swift/AST/Decl.h" -#include "swift/AST/Types.h" -#include "swift/IDE/CodeCompletionResultPrinter.h" -#include "swift/IDE/CodeCompletionStringBuilder.h" -#include "swift/IDE/CommentConversion.h" #include "swift/IDE/SignatureHelp.h" +#include "swift/IDE/SignatureHelpFormatter.h" #include "swift/IDETool/IDEInspectionInstance.h" -#include "llvm/Support/Allocator.h" -#include "llvm/Support/raw_ostream.h" using namespace SourceKit; using namespace swift; using namespace ide; -using ChunkKind = CodeCompletionString::Chunk::ChunkKind; - -namespace { -struct SignatureInfo { - StringRef Text; - StringRef DocComment; - std::optional ActiveParam; - SmallVector Params; - - SignatureInfo() {} -}; -} // namespace - -/// \returns Array of parameters of \p VD accounting for implicitly curried -/// instance methods. -static ArrayRef -getParameterArray(const ValueDecl *VD, bool IsImplicitlyCurried, - const ParamDecl *&Scratch) { - if (!VD) - return {}; - - if (IsImplicitlyCurried) { - auto *FD = dyn_cast(VD); - assert(FD && FD->hasImplicitSelfDecl()); - - Scratch = FD->getImplicitSelfDecl(); - return ArrayRef(&Scratch, 1); - } - - if (auto *ParamList = VD->getParameterList()) - return ParamList->getArray(); - - return {}; -} - -static CodeCompletionString *createSignatureString( - llvm::BumpPtrAllocator &Allocator, ValueDecl *FD, AnyFunctionType *AFT, - const DeclContext *DC, GenericSignature GenericSig, bool IsSubscript, - bool IsMember, bool IsImplicitlyCurried, bool IsSecondApply) { - CodeCompletionStringBuilder StringBuilder( - Allocator, /*AnnotateResults=*/false, - /*UnderscoreEmptyArgumentLabel=*/!IsSubscript, - /*FullParameterFlags=*/true); - - DeclBaseName BaseName; - - if (!IsSecondApply && FD) { - BaseName = FD->getBaseName(); - } else if (IsSubscript) { - BaseName = DeclBaseName::createSubscript(); - } - - if (!BaseName.empty()) - StringBuilder.addValueBaseName(BaseName, IsMember); - - StringBuilder.addLeftParen(); - - const ParamDecl *ParamScratch; - StringBuilder.addCallArgumentPatterns( - AFT->getParams(), - getParameterArray(FD, IsImplicitlyCurried, ParamScratch), DC, GenericSig, - DefaultArgumentOutputMode::All, /*includeDefaultValues=*/true); - - StringBuilder.addRightParen(); - - if (!IsImplicitlyCurried) { - // For a second apply, we don't pass the declaration to avoid adding - // incorrect rethrows and reasync which are only usable in a single apply. - StringBuilder.addEffectsSpecifiers( - AFT, - /*AFD=*/IsSecondApply ? nullptr - : dyn_cast_or_null(FD)); - } - - if (FD && FD->isImplicitlyUnwrappedOptional()) { - StringBuilder.addTypeAnnotationForImplicitlyUnwrappedOptional( - AFT->getResult(), DC, GenericSig); - } else { - StringBuilder.addTypeAnnotation(AFT->getResult(), DC, GenericSig); - } - - return StringBuilder.createCompletionString(); -} - -static StringRef copyAndClearString(llvm::BumpPtrAllocator &Allocator, - SmallVectorImpl &Str) { - auto Ref = StringRef(Str.data(), Str.size()).copy(Allocator); - Str.clear(); - return Ref; -} - -static void getSignatureInfo(const DeclContext *DC, const Signature &Sig, - SignatureInfo &Info, - llvm::BumpPtrAllocator &Allocator) { - auto *FD = Sig.FuncD; - auto *AFT = Sig.FuncTy; - - bool IsConstructor = false; - GenericSignature genericSig; - if (FD) { - IsConstructor = isa(FD); - - if (auto *GC = FD->getAsGenericContext()) - genericSig = GC->getGenericSignature(); - } - - auto *SignatureString = - createSignatureString(Allocator, FD, AFT, DC, genericSig, Sig.IsSubscript, - /*IsMember=*/bool(Sig.BaseType), - Sig.IsImplicitlyCurried, Sig.IsSecondApply); - - llvm::SmallString<512> SS; - llvm::raw_svector_ostream OS(SS); - - bool SkipResult = AFT->getResult()->isVoid() || IsConstructor; - - auto Chunks = SignatureString->getChunks(); - auto C = Chunks.begin(); - while (C != Chunks.end()) { - if (C->is(ChunkKind::TypeAnnotation) && SkipResult) { - ++C; - continue; - } - - if (C->is(ChunkKind::TypeAnnotation)) - OS << " -> "; - - if (C->is(ChunkKind::CallArgumentBegin)) { - unsigned NestingLevel = C->getNestingLevel(); - ++C; - - auto &P = Info.Params.emplace_back(); - P.Offset = SS.size(); - - do { - if (!C->is(ChunkKind::CallArgumentClosureType) && C->hasText()) - OS << C->getText(); - - ++C; - } while (C != Chunks.end() && !C->endsPreviousNestedGroup(NestingLevel)); - - P.Length = SS.size() - P.Offset; - continue; - } - - if (C->hasText()) - OS << C->getText(); - - ++C; - } - - Info.Text = copyAndClearString(Allocator, SS); - Info.ActiveParam = Sig.ParamIdx; - - // Parameter names. - const ParamDecl *ParamScratch; - auto ParamDecls = - getParameterArray(FD, Sig.IsImplicitlyCurried, ParamScratch); - - if (!ParamDecls.empty()) { - for (unsigned i = 0; i < Info.Params.size(); ++i) { - Info.Params[i].Name = ParamDecls[i]->getParameterName().str(); - } - } - - // Documentation. - if (FD) { - ide::getRawDocumentationComment(FD, OS); - - Info.DocComment = copyAndClearString(Allocator, SS); - } -} - static void deliverResults(SourceKit::SignatureHelpConsumer &SKConsumer, CancellableResult Result) { switch (Result.getKind()) { @@ -212,29 +33,9 @@ static void deliverResults(SourceKit::SignatureHelpConsumer &SKConsumer, } llvm::BumpPtrAllocator Allocator; - SmallVector Infos; - - for (auto &Sig : Result->Result->Signatures) { - Infos.emplace_back(); - auto &Info = Infos.back(); - - getSignatureInfo(Result->Result->DC, Sig, Info, Allocator); - } - - SourceKit::SignatureHelpResponse SKResult; - SmallVector SKSignatures; - - for (auto &Info : Infos) { - SKSignatures.push_back( - {Info.Text, Info.DocComment, Info.ActiveParam, Info.Params}); - } - - SKResult.Signatures = SKSignatures; - - // TODO(a7medev): Select active signature and param. - SKResult.ActiveSignature = 0; + SignatureHelpFormatter Formatter(Allocator); - SKConsumer.handleResult(SKResult); + SKConsumer.handleResult(Formatter.format(*Result->Result)); break; } case CancellableResultKind::Failure: diff --git a/tools/SourceKit/tools/sourcekitd/lib/Service/Requests.cpp b/tools/SourceKit/tools/sourcekitd/lib/Service/Requests.cpp index 216b78d9e7e3a..3138558df83d9 100644 --- a/tools/SourceKit/tools/sourcekitd/lib/Service/Requests.cpp +++ b/tools/SourceKit/tools/sourcekitd/lib/Service/Requests.cpp @@ -36,6 +36,7 @@ #include "swift/Basic/LLVMInitialize.h" #include "swift/Basic/Statistic.h" #include "swift/Basic/Version.h" +#include "swift/IDE/SignatureHelpFormatter.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" @@ -3645,7 +3646,8 @@ signatureHelp(StringRef PrimaryFilePath, int64_t Offset, public: Consumer(ResponseBuilder Builder) : SKResult(Builder.getDictionary()) {} - void handleResult(const SignatureHelpResponse &Result) override { + void + handleResult(const swift::ide::FormattedSignatureHelp &Result) override { SKResult.set(KeyActiveSignature, Result.ActiveSignature); auto Signatures = SKResult.setArray(KeySignatures); @@ -3658,8 +3660,8 @@ signatureHelp(StringRef PrimaryFilePath, int64_t Offset, if (auto ActiveParam = Signature.ActiveParam) SignatureElem.set(KeyActiveParameter, ActiveParam.value()); - if (!Signature.Doc.empty()) - SignatureElem.set(KeyDocComment, Signature.Doc); + if (!Signature.DocComment.empty()) + SignatureElem.set(KeyDocComment, Signature.DocComment); auto Params = SignatureElem.setArray(KeyParameters); @@ -3671,9 +3673,6 @@ signatureHelp(StringRef PrimaryFilePath, int64_t Offset, ParamElem.set(KeyNameOffset, Param.Offset); ParamElem.set(KeyNameLength, Param.Length); - - if (!Param.DocComment.empty()) - ParamElem.set(KeyDocComment, Param.DocComment); } } } diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index a629fe5b1a429..19f5a49fb30f4 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -43,6 +43,8 @@ #include "swift/IDE/IDERequests.h" #include "swift/IDE/ModuleInterfacePrinting.h" #include "swift/IDE/REPLCodeCompletion.h" +#include "swift/IDE/SignatureHelp.h" +#include "swift/IDE/SignatureHelpFormatter.h" #include "swift/IDE/SourceEntityWalker.h" #include "swift/IDE/SyntaxModel.h" #include "swift/IDE/TypeContextInfo.h" @@ -117,6 +119,7 @@ enum class ActionType { Range, TypeContextInfo, ConformingMethodList, + SignatureHelp, }; class NullDebuggerClient : public DebuggerClient { @@ -251,7 +254,9 @@ Action(llvm::cl::desc("Mode:"), llvm::cl::init(ActionType::None), "Print types for all expressions in the file"), clEnumValN(ActionType::ConformingMethodList, "conforming-methods", - "Perform conforming method analysis for expression"))); + "Perform conforming method analysis for expression"), + clEnumValN(ActionType::SignatureHelp, "signature-help", + "Perform signature help"))); static llvm::cl::opt SourceFilename("source-filename", llvm::cl::desc("Name of the source file"), @@ -1446,6 +1451,99 @@ printCodeCompletionLookedupTypeNames(ArrayRef names, OS << "]\n"; } +static void printWithEscaping(StringRef Str, llvm::raw_ostream &OS) { + for (char C : Str) { + switch (C) { + case '\n': + OS << "\\n"; + break; + case '\r': + OS << "\\r"; + break; + case '\t': + OS << "\\t"; + break; + case '\v': + OS << "\\v"; + break; + case '\f': + OS << "\\f"; + break; + default: + OS << C; + break; + } + } +} + +static void printSignatureHelpResultsImpl(const FormattedSignatureHelp &Result, + llvm::raw_ostream &OS) { + OS << "Begin signatures, " << Result.Signatures.size() << " items\n"; + + for (unsigned i = 0; i < Result.Signatures.size(); ++i) { + const auto &Signature = Result.Signatures[i]; + if (i == Result.ActiveSignature) { + OS << "Signature[Active]: "; + } else { + OS << "Signature: "; + } + + StringRef signatureText = Signature.Text; + + unsigned currentPos = 0; + for (unsigned j = 0; j < Signature.Params.size(); ++j) { + const auto &Param = Signature.Params[j]; + + if (Param.Offset > currentPos) { + OS << signatureText.substr(currentPos, Param.Offset - currentPos); + } + + OS << ""; + OS << signatureText.substr(Param.Offset, Param.Length); + OS << ""; + + currentPos = Param.Offset + Param.Length; + } + + if (currentPos < signatureText.size()) { + OS << signatureText.substr(currentPos); + } + + if (!Signature.DocComment.empty()) { + OS << "; Documentation="; + printWithEscaping(Signature.DocComment, OS); + } + + OS << "\n"; + } + + OS << "End signatures\n"; +} + +static int printSignatureHelpResults( + CancellableResult CancellableResult) { + llvm::raw_fd_ostream &OS = llvm::outs(); + return printResult( + CancellableResult, [&](const SignatureHelpResults &Results) { + if (Results.Result) { + llvm::BumpPtrAllocator Allocator; + SignatureHelpFormatter Formatter(Allocator); + auto FormattedResult = Formatter.format(*Results.Result); + printSignatureHelpResultsImpl(FormattedResult, OS); + } else { + OS << "No signature help results\n"; + } + return 0; + }); +} + static int printCodeCompletionResults( CancellableResult CancellableResult, bool IncludeKeywords, bool IncludeComments, bool IncludeSourceText, @@ -1463,6 +1561,27 @@ static int printCodeCompletionResults( }); } +static int doSignatureHelp(const CompilerInvocation &InitInvok, + StringRef SourceFilename, + StringRef SecondSourceFileName, + StringRef SignatureHelpToken, + bool SignatureHelpDiagnostics) { + return performWithCompletionLikeOperationParams( + InitInvok, SourceFilename, SecondSourceFileName, SignatureHelpToken, + SignatureHelpDiagnostics, + [&](CompletionLikeOperationParams Params) -> bool { + IDEInspectionInstance Inst(std::make_shared()); + int ExitCode = 2; + Inst.signatureHelp(Params.Invocation, Params.Args, Params.FileSystem, + Params.CompletionBuffer, Params.Offset, Params.DiagC, + /*CancellationFlag=*/nullptr, + [&](CancellableResult Result) { + ExitCode = printSignatureHelpResults(Result); + }); + return ExitCode; + }); +} + static int doCodeCompletion(const CompilerInvocation &InitInvok, StringRef SourceFilename, StringRef SecondSourceFileName, StringRef CodeCompletionToken, @@ -3470,19 +3589,6 @@ class ASTCommentPrinter : public ASTWalker { ASTCommentPrinter(SourceManager &SM, XMLValidator &TheXMLValidator) : OS(llvm::outs()), SM(SM), TheXMLValidator(TheXMLValidator) {} - void printWithEscaping(StringRef Str) { - for (char C : Str) { - switch (C) { - case '\n': OS << "\\n"; break; - case '\r': OS << "\\r"; break; - case '\t': OS << "\\t"; break; - case '\v': OS << "\\v"; break; - case '\f': OS << "\\f"; break; - default: OS << C; break; - } - } - } - void printDeclName(const ValueDecl *VD) { if (auto *NTD = dyn_cast(VD->getDeclContext())) { Identifier Id = NTD->getName(); @@ -3555,7 +3661,7 @@ class ASTCommentPrinter : public ASTWalker { } OS << "["; for (auto &SRC : RC.Comments) - printWithEscaping(SRC.RawText); + printWithEscaping(SRC.RawText, OS); OS << "]"; } @@ -3566,7 +3672,7 @@ class ASTCommentPrinter : public ASTWalker { return; } OS << "["; - printWithEscaping(Brief); + printWithEscaping(Brief, OS); OS << "]"; } @@ -3582,7 +3688,7 @@ class ASTCommentPrinter : public ASTWalker { return; } OS << "["; - printWithEscaping(XML); + printWithEscaping(XML, OS); OS << "]"; auto Status = TheXMLValidator.validate(XML); @@ -4849,6 +4955,16 @@ int main(int argc, char *argv[]) { options::ConformingMethodListExpectedTypes); break; + case ActionType::SignatureHelp: + if (options::CodeCompletionToken.empty()) { + llvm::errs() << "signature help token name required\n"; + return 1; + } + ExitCode = doSignatureHelp( + InitInvok, options::SourceFilename, options::SecondSourceFilename, + options::CodeCompletionToken, options::CodeCompletionDiagnostics); + break; + case ActionType::SyntaxColoring: ExitCode = doSyntaxColoring(InitInvok, options::SourceFilename,