@@ -31,6 +31,7 @@ import (
3131 "github.com/bufbuild/buf/private/pkg/slicesext"
3232 "github.com/bufbuild/protocompile/ast"
3333 "go.lsp.dev/protocol"
34+ "google.golang.org/protobuf/encoding/protowire"
3435)
3536
3637// symbol represents a named symbol inside of a buflsp.file
@@ -96,10 +97,16 @@ type builtin struct {
9697 name string
9798}
9899
100+ type fieldTag struct {
101+ tag ast.IntValueNode
102+ def * symbol
103+ }
104+
99105func (* definition ) isSymbolKind () {}
100106func (* reference ) isSymbolKind () {}
101107func (* import_ ) isSymbolKind () {}
102108func (* builtin ) isSymbolKind () {}
109+ func (* fieldTag ) isSymbolKind () {}
103110
104111// Range constructs an LSP protocol code range for this symbol.
105112func (s * symbol ) Range () protocol.Range {
@@ -403,6 +410,72 @@ func (s *symbol) FormatDocs(ctx context.Context) string {
403410 node = kind .node
404411 path = kind .path
405412
413+ case * import_ :
414+ return fmt .Sprintf ("```proto\n %s\n ```" , kind .file .text )
415+
416+ case * fieldTag :
417+ var value uint64
418+ if v , ok := kind .tag .AsInt64 (); ok {
419+ value = uint64 (v )
420+ } else {
421+ value , _ = kind .tag .AsUint64 ()
422+ }
423+
424+ plural := func (i int ) string {
425+ if i == 1 {
426+ return ""
427+ }
428+ return "s"
429+ }
430+
431+ var ty protowire.Type
432+ var packed bool
433+ // Only definition symbols are placed into the def field of fieldTag, so
434+ // doing an unchecked assertion here is ok.
435+ switch def := kind .def .kind .(* definition ).node .(type ) {
436+ case * ast.EnumValueNode :
437+ varint := protowire .AppendVarint (nil , value )
438+ return fmt .Sprintf (
439+ "`0x%x`, `0b%b`\n \n encoded (hex): `%X` (%d byte%s)" ,
440+ value , value , varint , len (varint ), plural (len (varint )),
441+ )
442+ case * ast.MapFieldNode :
443+ ty = protowire .BytesType
444+ case * ast.GroupNode :
445+ ty = protowire .StartGroupType
446+ case * ast.FieldNode :
447+ switch def .FldType .AsIdentifier () {
448+ case "bool" , "int32" , "int64" , "uint32" , "uint64" , "sint32" , "sint64" :
449+ ty = protowire .VarintType
450+ packed = def .Label .Repeated
451+ case "fixed32" , "sfixed32" , "float" :
452+ ty = protowire .Fixed32Type
453+ packed = def .Label .Repeated
454+ case "fixed64" , "sfixed64" , "double" :
455+ ty = protowire .Fixed64Type
456+ packed = def .Label .Repeated
457+ default :
458+ ty = protowire .BytesType
459+ }
460+ }
461+
462+ // Don't use AppendTag because that wants to truncate value to int32.
463+ varint := protowire .AppendVarint (nil , value << 3 | uint64 (ty ))
464+ doc := fmt .Sprintf (
465+ "encoded (hex): `%X` (%d byte%s)" ,
466+ varint , len (varint ), plural (len (varint )),
467+ )
468+
469+ if packed {
470+ packed := protowire .AppendVarint (nil , value << 3 | uint64 (protowire .BytesType ))
471+ return doc + fmt .Sprintf (
472+ "\n \n packed (hex): `%X` (%d byte%s)" ,
473+ packed , len (packed ), plural (len (varint )),
474+ )
475+ }
476+
477+ return doc
478+
406479 default :
407480 return ""
408481 }
@@ -597,25 +670,29 @@ func (w *symbolWalker) Walk(node, parent ast.Node) {
597670 }
598671
599672 case * ast.GroupNode :
673+ def := w .newDef (node , node .Name )
600674 w .newDef (node , node .Name )
675+ w .newTag (node .Tag , def )
601676 // TODO: also do the name of the generated field.
602677 for _ , decl := range node .Decls {
603678 w .Walk (decl , node )
604679 }
605680
606681 case * ast.FieldNode :
607- w .newDef (node , node .Name )
682+ def := w .newDef (node , node .Name )
608683 w .newRef (node .FldType )
684+ w .newTag (node .Tag , def )
609685 if node .Options != nil {
610686 for _ , option := range node .Options .Options {
611687 w .Walk (option , node )
612688 }
613689 }
614690
615691 case * ast.MapFieldNode :
616- w .newDef (node , node .Name )
692+ def := w .newDef (node , node .Name )
617693 w .newRef (node .MapType .KeyType )
618694 w .newRef (node .MapType .ValueType )
695+ w .newTag (node .Tag , def )
619696 if node .Options != nil {
620697 for _ , option := range node .Options .Options {
621698 w .Walk (option , node )
@@ -639,7 +716,8 @@ func (w *symbolWalker) Walk(node, parent ast.Node) {
639716 }
640717
641718 case * ast.EnumValueNode :
642- w .newDef (node , node .Name )
719+ def := w .newDef (node , node .Name )
720+ w .newTag (node .Number , def )
643721 if node .Options != nil {
644722 for _ , option := range node .Options .Options {
645723 w .Walk (option , node )
@@ -713,6 +791,15 @@ func (w *symbolWalker) newDef(node ast.Node, name *ast.IdentNode) *symbol {
713791 return symbol
714792}
715793
794+ // newTag creates a new symbol for a field tag, and adds it to the running list.
795+ //
796+ // Returns a new symbol for that tag.
797+ func (w * symbolWalker ) newTag (tag ast.IntValueNode , def * symbol ) * symbol {
798+ symbol := w .newSymbol (tag )
799+ symbol .kind = & fieldTag {tag , def }
800+ return symbol
801+ }
802+
716803// newDef creates a new symbol for a name reference, and adds it to the running list.
717804//
718805// newRef performs same-file Protobuf name resolution. It searches for a partial package
0 commit comments