Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions private/buf/buflsp/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,7 @@ func completionItemsForDef(ctx context.Context, file *file, declPath []ast.DeclA
tokenSpan,
offset,
),
rpcRequestResponseSnippetCompletionItem(parentDef, tokenSpan),
)
}
} else if showReturnKeyword {
Expand Down Expand Up @@ -867,6 +868,50 @@ func keywordToCompletionItem(
}
}

// rpcRequestResponseSnippetCompletionItem returns a completion item for the common RPC pattern.
// This generates an RPC method declaration with Request/Response message types.
func rpcRequestResponseSnippetCompletionItem(
parentDef ast.DeclDef,
tokenSpan source.Span,
) iter.Seq[protocol.CompletionItem] {
return func(yield func(protocol.CompletionItem) bool) {
// Create expanded span from cursor (offset) to end of service
serviceEndOffset := parentDef.Span().End
expandedSpan := source.Span{
File: tokenSpan.File,
Start: tokenSpan.End,
End: serviceEndOffset,
}
body := expandedSpan.Text()

// The snippet uses ${1:Name} for linked placeholders.
// Because everything is in one TextEdit, placeholders work across RPC and messages.
var sb strings.Builder
sb.WriteString("rpc ${1:Name}(${1:Name}Request) returns (${1:Name}Response);")
sb.WriteString(body)
sb.WriteString("\n\nmessage ${1:Name}Request {}\n\nmessage ${1:Name}Response {}")

yield(protocol.CompletionItem{
Label: "rpc Name(NameRequest) returns (NameResponse)",
Kind: protocol.CompletionItemKindSnippet,
InsertTextFormat: protocol.InsertTextFormatSnippet,
InsertTextMode: protocol.InsertTextModeAsIs,
Detail: "Insert RPC method with Request and Response messages",
Documentation: "Creates an RPC method declaration and corresponding Request/Response message types",
TextEdit: &protocol.TextEdit{
Range: reportSpanToProtocolRange(tokenSpan),
NewText: sb.String(),
},
AdditionalTextEdits: []protocol.TextEdit{
{
Range: reportSpanToProtocolRange(expandedSpan),
NewText: "", // Delete from cursor to end of service
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete is required for VSCode. If the TextEdit.Range spans the tokenSpan to the serviceOffset it rejects the completion item.

},
},
})
}
}

// typeReferencesToCompletionItems returns completion items for user-defined types (messages, enums, etc).
func typeReferencesToCompletionItems(
current *file,
Expand Down