Skip to content

Commit 2f60646

Browse files
committed
[IDE] Move signature help formatting to IDE instead of SourceKit
1 parent 3e981b7 commit 2f60646

File tree

6 files changed

+310
-247
lines changed

6 files changed

+310
-247
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
//===--- SignatureHelpFormatter.h --- ---------------------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef SWIFT_IDE_SIGNATURE_HELP_FORMATTER_H
14+
#define SWIFT_IDE_SIGNATURE_HELP_FORMATTER_H
15+
16+
#include "swift/IDE/SignatureHelp.h"
17+
#include "llvm/ADT/ArrayRef.h"
18+
#include "llvm/ADT/StringRef.h"
19+
#include "llvm/Support/Allocator.h"
20+
21+
namespace swift {
22+
23+
class ValueDecl;
24+
class AnyFunctionType;
25+
class DeclContext;
26+
class GenericSignature;
27+
28+
namespace ide {
29+
30+
class CodeCompletionString;
31+
32+
struct FormattedSignatureHelp {
33+
struct Parameter {
34+
/// The offset of the parameter text in the signature text.
35+
unsigned Offset;
36+
37+
/// The length of the parameter text in the signature text.
38+
unsigned Length;
39+
40+
/// The internal parameter name.
41+
llvm::StringRef Name;
42+
43+
Parameter() {}
44+
};
45+
46+
struct Signature {
47+
llvm::StringRef Text;
48+
llvm::StringRef DocComment;
49+
std::optional<unsigned> ActiveParam;
50+
llvm::ArrayRef<Parameter> Params;
51+
52+
Signature(llvm::StringRef Text, llvm::StringRef DocComment,
53+
std::optional<unsigned> ActiveParam,
54+
llvm::ArrayRef<Parameter> Params)
55+
: Text(Text), DocComment(DocComment), ActiveParam(ActiveParam),
56+
Params(Params) {}
57+
};
58+
59+
llvm::ArrayRef<Signature> Signatures;
60+
unsigned ActiveSignature;
61+
62+
FormattedSignatureHelp(llvm::ArrayRef<Signature> Signatures,
63+
unsigned ActiveSignature)
64+
: Signatures(Signatures), ActiveSignature(ActiveSignature) {}
65+
};
66+
67+
class SignatureHelpFormatter {
68+
private:
69+
llvm::BumpPtrAllocator &Allocator;
70+
71+
public:
72+
SignatureHelpFormatter(llvm::BumpPtrAllocator &Allocator)
73+
: Allocator(Allocator) {}
74+
75+
FormattedSignatureHelp format(ide::SignatureHelpResult Result);
76+
77+
private:
78+
FormattedSignatureHelp::Signature
79+
formatSignature(const DeclContext *DC, const ide::Signature &Signature);
80+
81+
CodeCompletionString *
82+
createSignatureString(ValueDecl *FD, AnyFunctionType *AFT,
83+
const DeclContext *DC, GenericSignature GenericSig,
84+
bool IsSubscript, bool IsMember,
85+
bool IsImplicitlyCurried, bool IsSecondApply);
86+
};
87+
88+
} // namespace ide
89+
} // namespace swift
90+
91+
#endif // SWIFT_IDE_SIGNATURE_HELP_FORMATTER_H

lib/IDE/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ add_swift_host_library(swiftIDE STATIC
1919
CompletionOverrideLookup.cpp
2020
ConformingMethodList.cpp
2121
SignatureHelp.cpp
22+
SignatureHelpFormatter.cpp
2223
CursorInfo.cpp
2324
ExprCompletion.cpp
2425
ExprContextAnalysis.cpp

lib/IDE/SignatureHelpFormatter.cpp

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
//===--- SignatureHelpFormatter.cpp --- -------------------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "swift/IDE/SignatureHelpFormatter.h"
14+
#include "swift/AST/ParameterList.h"
15+
#include "swift/IDE/CodeCompletionStringBuilder.h"
16+
#include "swift/IDE/CommentConversion.h"
17+
18+
using namespace swift;
19+
using namespace swift::ide;
20+
21+
using ChunkKind = CodeCompletionString::Chunk::ChunkKind;
22+
23+
/// \returns Array of parameters of \p VD accounting for implicitly curried
24+
/// instance methods.
25+
static ArrayRef<const ParamDecl *>
26+
getParameterArray(const ValueDecl *VD, bool IsImplicitlyCurried,
27+
const ParamDecl *&Scratch) {
28+
if (!VD)
29+
return {};
30+
31+
if (IsImplicitlyCurried) {
32+
auto *FD = dyn_cast<AbstractFunctionDecl>(VD);
33+
assert(FD && FD->hasImplicitSelfDecl());
34+
35+
Scratch = FD->getImplicitSelfDecl();
36+
return ArrayRef(&Scratch, 1);
37+
}
38+
39+
if (auto *ParamList = VD->getParameterList())
40+
return ParamList->getArray();
41+
42+
return {};
43+
}
44+
45+
static StringRef copyAndClearString(llvm::BumpPtrAllocator &Allocator,
46+
SmallVectorImpl<char> &Str) {
47+
auto Ref = StringRef(Str.data(), Str.size()).copy(Allocator);
48+
Str.clear();
49+
return Ref;
50+
}
51+
52+
CodeCompletionString *SignatureHelpFormatter::createSignatureString(
53+
ValueDecl *FD, AnyFunctionType *AFT, const DeclContext *DC,
54+
GenericSignature GenericSig, bool IsSubscript, bool IsMember,
55+
bool IsImplicitlyCurried, bool IsSecondApply) {
56+
CodeCompletionStringBuilder StringBuilder(
57+
Allocator, /*AnnotateResults=*/false,
58+
/*UnderscoreEmptyArgumentLabel=*/!IsSubscript,
59+
/*FullParameterFlags=*/true);
60+
61+
DeclBaseName BaseName;
62+
63+
if (!IsSecondApply && FD) {
64+
BaseName = FD->getBaseName();
65+
} else if (IsSubscript) {
66+
BaseName = DeclBaseName::createSubscript();
67+
}
68+
69+
if (!BaseName.empty())
70+
StringBuilder.addValueBaseName(BaseName, IsMember);
71+
72+
StringBuilder.addLeftParen();
73+
74+
const ParamDecl *ParamScratch;
75+
StringBuilder.addCallArgumentPatterns(
76+
AFT->getParams(),
77+
getParameterArray(FD, IsImplicitlyCurried, ParamScratch), DC, GenericSig,
78+
DefaultArgumentOutputMode::All, /*includeDefaultValues=*/true);
79+
80+
StringBuilder.addRightParen();
81+
82+
if (!IsImplicitlyCurried) {
83+
// For a second apply, we don't pass the declaration to avoid adding
84+
// incorrect rethrows and reasync which are only usable in a single apply.
85+
StringBuilder.addEffectsSpecifiers(
86+
AFT,
87+
/*AFD=*/IsSecondApply ? nullptr
88+
: dyn_cast_or_null<AbstractFunctionDecl>(FD));
89+
}
90+
91+
if (FD && FD->isImplicitlyUnwrappedOptional()) {
92+
StringBuilder.addTypeAnnotationForImplicitlyUnwrappedOptional(
93+
AFT->getResult(), DC, GenericSig);
94+
} else {
95+
StringBuilder.addTypeAnnotation(AFT->getResult(), DC, GenericSig);
96+
}
97+
98+
return StringBuilder.createCompletionString();
99+
}
100+
101+
FormattedSignatureHelp::Signature
102+
SignatureHelpFormatter::formatSignature(const DeclContext *DC,
103+
const ide::Signature &Signature) {
104+
auto *FD = Signature.FuncD;
105+
auto *AFT = Signature.FuncTy;
106+
107+
bool IsConstructor = false;
108+
GenericSignature genericSig;
109+
if (FD) {
110+
IsConstructor = isa<ConstructorDecl>(FD);
111+
112+
if (auto *GC = FD->getAsGenericContext())
113+
genericSig = GC->getGenericSignature();
114+
}
115+
116+
auto *SignatureString = createSignatureString(
117+
FD, AFT, DC, genericSig, Signature.IsSubscript,
118+
/*IsMember=*/bool(Signature.BaseType), Signature.IsImplicitlyCurried,
119+
Signature.IsSecondApply);
120+
121+
llvm::SmallString<512> SS;
122+
llvm::raw_svector_ostream OS(SS);
123+
124+
bool SkipResult = AFT->getResult()->isVoid() || IsConstructor;
125+
126+
SmallVector<FormattedSignatureHelp::Parameter, 8> FormattedParams;
127+
128+
auto Chunks = SignatureString->getChunks();
129+
auto C = Chunks.begin();
130+
while (C != Chunks.end()) {
131+
if (C->is(ChunkKind::TypeAnnotation) && SkipResult) {
132+
++C;
133+
continue;
134+
}
135+
136+
if (C->is(ChunkKind::TypeAnnotation))
137+
OS << " -> ";
138+
139+
if (C->is(ChunkKind::CallArgumentBegin)) {
140+
unsigned NestingLevel = C->getNestingLevel();
141+
++C;
142+
143+
auto &P = FormattedParams.emplace_back();
144+
P.Offset = SS.size();
145+
146+
do {
147+
if (!C->is(ChunkKind::CallArgumentClosureType) && C->hasText())
148+
OS << C->getText();
149+
150+
++C;
151+
} while (C != Chunks.end() && !C->endsPreviousNestedGroup(NestingLevel));
152+
153+
P.Length = SS.size() - P.Offset;
154+
continue;
155+
}
156+
157+
if (C->hasText())
158+
OS << C->getText();
159+
160+
++C;
161+
}
162+
163+
StringRef SignatureText = copyAndClearString(Allocator, SS);
164+
165+
// Parameter names.
166+
const ParamDecl *ParamScratch;
167+
auto ParamDecls =
168+
getParameterArray(FD, Signature.IsImplicitlyCurried, ParamScratch);
169+
170+
if (!ParamDecls.empty()) {
171+
for (unsigned i = 0; i < FormattedParams.size(); ++i) {
172+
FormattedParams[i].Name = ParamDecls[i]->getParameterName().str();
173+
}
174+
}
175+
176+
// Documentation.
177+
StringRef DocComment;
178+
if (FD) {
179+
ide::getRawDocumentationComment(FD, OS);
180+
DocComment = copyAndClearString(Allocator, SS);
181+
}
182+
183+
return FormattedSignatureHelp::Signature(
184+
SignatureText, DocComment, Signature.ParamIdx,
185+
ArrayRef(FormattedParams).copy(Allocator));
186+
}
187+
188+
FormattedSignatureHelp
189+
SignatureHelpFormatter::format(SignatureHelpResult Result) {
190+
SmallVector<FormattedSignatureHelp::Signature, 8> FormattedSignatures;
191+
FormattedSignatures.reserve(Result.Signatures.size());
192+
193+
for (auto &Signature : Result.Signatures) {
194+
FormattedSignatures.push_back(formatSignature(Result.DC, Signature));
195+
}
196+
197+
// FIXME: Ideally we should select an active signature based on the context.
198+
unsigned ActiveSignature = 0;
199+
200+
return FormattedSignatureHelp(ArrayRef(FormattedSignatures).copy(Allocator),
201+
ActiveSignature);
202+
}

tools/SourceKit/include/SourceKit/Core/LangSupport.h

Lines changed: 8 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ namespace llvm {
3535
class MemoryBuffer;
3636
}
3737

38+
namespace swift {
39+
namespace ide {
40+
struct FormattedSignatureHelp;
41+
} // namespace ide
42+
} // namespace swift
43+
3844
namespace SourceKit {
3945
class GlobalConfig;
4046
using swift::ide::CancellableResult;
@@ -1012,51 +1018,14 @@ class ConformingMethodListConsumer {
10121018
virtual void cancelled() = 0;
10131019
};
10141020

1015-
struct SignatureHelpResponse {
1016-
struct Parameter {
1017-
/// The offset of the parameter text in the signature text.
1018-
unsigned Offset;
1019-
1020-
/// The length of the parameter text in the signature text.
1021-
unsigned Length;
1022-
1023-
/// The documentation comment for the parameter.
1024-
StringRef DocComment;
1025-
1026-
/// The internal parameter name.
1027-
StringRef Name;
1028-
1029-
Parameter() {}
1030-
};
1031-
1032-
struct Signature {
1033-
/// The text describing the signature.
1034-
StringRef Text;
1035-
1036-
/// The documentation comment for the signature.
1037-
StringRef Doc;
1038-
1039-
/// The index of the active parameter if any.
1040-
std::optional<unsigned> ActiveParam;
1041-
1042-
/// The parameters for the signature.
1043-
ArrayRef<Parameter> Params;
1044-
};
1045-
1046-
/// The index of the active signature.
1047-
unsigned ActiveSignature;
1048-
1049-
/// The available signatures/overloads.
1050-
ArrayRef<Signature> Signatures;
1051-
};
1052-
10531021
class SignatureHelpConsumer {
10541022
virtual void anchor();
10551023

10561024
public:
10571025
virtual ~SignatureHelpConsumer() {}
10581026

1059-
virtual void handleResult(const SignatureHelpResponse &Result) = 0;
1027+
virtual void
1028+
handleResult(const swift::ide::FormattedSignatureHelp &Result) = 0;
10601029
virtual void setReusingASTContext(bool flag) = 0;
10611030
virtual void failed(StringRef ErrDescription) = 0;
10621031
virtual void cancelled() = 0;

0 commit comments

Comments
 (0)