-
Notifications
You must be signed in to change notification settings - Fork 10.6k
[IDE] Move signature help formatting to IDE instead of SourceKit #84403
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
hamishknight
merged 7 commits into
swiftlang:main
from
a7medev:refactor/signature-help-to-ide
Sep 29, 2025
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
2f60646
[IDE] Move signature help formatting to IDE instead of SourceKit
a7medev 85bf842
[Test] Add signature help to swift-ide-test
a7medev 979ccd1
[Test] Use swift-ide-test for signature help tests
a7medev 236fed2
[Test] Remove redundant signature parameter name="" in swift-ide-test
a7medev 0b12334
[IDE] Move CodeCompletionStringBuilder.h to lib/IDE
a7medev 6f6b9d8
[IDE] Simplify createSignatureString parameters
a7medev 456a40a
[Test] Improve signature help tests
a7medev File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<unsigned> ActiveParam; | ||
llvm::ArrayRef<Parameter> Params; | ||
|
||
Signature(llvm::StringRef Text, llvm::StringRef DocComment, | ||
std::optional<unsigned> ActiveParam, | ||
llvm::ArrayRef<Parameter> Params) | ||
: Text(Text), DocComment(DocComment), ActiveParam(ActiveParam), | ||
Params(Params) {} | ||
}; | ||
|
||
llvm::ArrayRef<Signature> Signatures; | ||
unsigned ActiveSignature; | ||
|
||
FormattedSignatureHelp(llvm::ArrayRef<Signature> Signatures, | ||
unsigned ActiveSignature) | ||
: Signatures(Signatures), ActiveSignature(ActiveSignature) {} | ||
}; | ||
|
||
class SignatureHelpFormatter { | ||
private: | ||
llvm::BumpPtrAllocator &Allocator; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<const ParamDecl *> | ||
getParameterArray(const ValueDecl *VD, bool IsImplicitlyCurried, | ||
const ParamDecl *&Scratch) { | ||
if (!VD) | ||
return {}; | ||
|
||
if (IsImplicitlyCurried) { | ||
auto *FD = dyn_cast<AbstractFunctionDecl>(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<char> &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<AbstractFunctionDecl>(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<ConstructorDecl>(FD); | ||
|
||
auto *SignatureString = createSignatureString(Signature, DC); | ||
|
||
llvm::SmallString<512> SS; | ||
llvm::raw_svector_ostream OS(SS); | ||
|
||
bool SkipResult = AFT->getResult()->isVoid() || IsConstructor; | ||
|
||
SmallVector<FormattedSignatureHelp::Parameter, 8> 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<FormattedSignatureHelp::Signature, 8> 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); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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, Result>(value: Value, body: (Value) -> Result) -> Result { | ||
return body(#^CLOSURE_PARAM^#) | ||
// CLOSURE_PARAM: Begin signatures, 1 items | ||
// CLOSURE_PARAM-DAG: Signature[Active]: body(<param active>Value</param>) -> Result | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed the
DocComment
field since we now rely on SourceKit-LSP to parse the signature's documentation and extract the parameter documentation.