Skip to content

Commit 0e1fad4

Browse files
authored
Add links in docs for predeclared types, remote dependencies, and WKTs (#4137)
1 parent 3561bf2 commit 0e1fad4

File tree

1 file changed

+75
-13
lines changed

1 file changed

+75
-13
lines changed

private/buf/buflsp/symbol.go

Lines changed: 75 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"slices"
2727
"strings"
2828

29+
"github.com/bufbuild/buf/private/bufpkg/bufmodule"
2930
"github.com/bufbuild/protocompile/experimental/ast"
3031
"github.com/bufbuild/protocompile/experimental/ast/predeclared"
3132
"github.com/bufbuild/protocompile/experimental/ir"
@@ -190,7 +191,6 @@ func (s *symbol) LogValue() slog.Value {
190191
//
191192
// Returns the empty string if no docs are available.
192193
func (s *symbol) FormatDocs() string {
193-
var def ast.DeclDef
194194
switch s.kind.(type) {
195195
case *imported:
196196
imported, _ := s.kind.(*imported)
@@ -257,20 +257,21 @@ func (s *symbol) FormatDocs() string {
257257
builtin, _ := s.kind.(*builtin)
258258
comments, ok := builtinDocs[builtin.predeclared.String()]
259259
if ok {
260+
comments = append(
261+
comments,
262+
"",
263+
fmt.Sprintf(
264+
"`%s` is a Protobuf builtin. [Learn more on protobuf.com.](https://protobuf.com/docs/language-spec#field-types)",
265+
builtin.predeclared,
266+
),
267+
)
260268
return strings.Join(comments, "\n")
261269
}
262270
return ""
263-
case *referenceable:
264-
referenceable, _ := s.kind.(*referenceable)
265-
def = referenceable.ast
266-
case *static:
267-
static, _ := s.kind.(*static)
268-
def = static.ast
269-
case *reference:
270-
reference, _ := s.kind.(*reference)
271-
def = reference.def
271+
case *referenceable, *static, *reference:
272+
return s.getDocsFromComments()
272273
}
273-
return getCommentsFromDef(def)
274+
return ""
274275
}
275276

276277
// GetSymbolInformation returns the protocol symbol information for the symbol.
@@ -343,10 +344,27 @@ func protowireTypeForPredeclared(name predeclared.Name) protowire.Type {
343344
return protowire.BytesType
344345
}
345346

346-
func getCommentsFromDef(def ast.DeclDef) string {
347+
// getDocsFromComments is a helper function that gets the doc string from the comments from
348+
// the definition AST, if available.
349+
// This helper function expects that imports, tags, and predeclared (builtin) types are
350+
// already handled, since those types currently do not get docs from their comments.
351+
func (s *symbol) getDocsFromComments() string {
352+
var def ast.DeclDef
353+
switch s.kind.(type) {
354+
case *referenceable:
355+
referenceable, _ := s.kind.(*referenceable)
356+
def = referenceable.ast
357+
case *static:
358+
static, _ := s.kind.(*static)
359+
def = static.ast
360+
case *reference:
361+
reference, _ := s.kind.(*reference)
362+
def = reference.def
363+
}
347364
if def.IsZero() {
348365
return ""
349366
}
367+
350368
var comments []string
351369
// We drop the other side of "Around" because we only care about the beginning -- we're
352370
// traversing backwards for leading comemnts only.
@@ -365,7 +383,51 @@ func getCommentsFromDef(def ast.DeclDef) string {
365383
}
366384
// Reverse the list and return joined.
367385
slices.Reverse(comments)
368-
return strings.Join(comments, "")
386+
387+
var docs strings.Builder
388+
for _, comment := range comments {
389+
docs.WriteString(comment)
390+
}
391+
392+
// If the file is a remote dependency, link to BSR docs.
393+
if s.def.file != nil && !s.def.file.IsLocal() {
394+
// In the BSR, messages, enums, and service definitions support anchor tags in the link.
395+
// Otherwise, we use the anchor for the parent type.
396+
var hasAnchor, isExtension bool
397+
switch def.Classify() {
398+
case ast.DefKindMessage, ast.DefKindEnum, ast.DefKindService:
399+
hasAnchor = true
400+
case ast.DefKindExtend:
401+
isExtension = !def.AsExtend().Extendee.IsZero()
402+
hasAnchor = isExtension
403+
}
404+
405+
var bsrHost, bsrAnchor string
406+
if s.def.file.IsWKT() {
407+
bsrHost = "buf.build/protocolbuffers/wellknowntypes"
408+
} else if fileInfo, ok := s.def.file.objectInfo.(bufmodule.FileInfo); ok {
409+
bsrHost = fileInfo.Module().FullName().String()
410+
}
411+
defFullName := s.def.ir.FullName()
412+
if !hasAnchor {
413+
defFullName = defFullName.Parent()
414+
}
415+
bsrAnchor = string(defFullName)
416+
// For extensions, we use the anchor for the extensions section in the BSR docs.
417+
if isExtension {
418+
bsrAnchor = "extensions"
419+
}
420+
if bsrHost != "" {
421+
docs.WriteString(fmt.Sprintf(
422+
"\n[`%s` on the Buf Schema Registry](https://%s/docs/main:%s#%s)",
423+
defFullName,
424+
bsrHost,
425+
s.def.file.ir.Package(),
426+
bsrAnchor,
427+
))
428+
}
429+
}
430+
return docs.String()
369431
}
370432

371433
// commentToMarkdown processes comment strings and formats them for markdown display.

0 commit comments

Comments
 (0)