1package protoprint
2
3import (
4	"bytes"
5	"fmt"
6	"io"
7	"math"
8	"os"
9	"path/filepath"
10	"reflect"
11	"sort"
12	"strings"
13
14	"github.com/golang/protobuf/proto"
15	"github.com/golang/protobuf/protoc-gen-go/descriptor"
16
17	"github.com/jhump/protoreflect/desc"
18	"github.com/jhump/protoreflect/desc/internal"
19	"github.com/jhump/protoreflect/dynamic"
20)
21
22// Printer knows how to format file descriptors as proto source code. Its fields
23// provide some control over how the resulting source file is constructed and
24// formatted.
25type Printer struct {
26	// If true, comments are rendered using "/*" style comments. Otherwise, they
27	// are printed using "//" style line comments.
28	PreferMultiLineStyleComments bool
29
30	// If true, elements are sorted into a canonical order.
31	//
32	// The canonical order for elements in a file follows:
33	//  1. Syntax
34	//  2. Package
35	//  3. Imports (sorted lexically)
36	//  4. Options (sorted by name, standard options before custom options)
37	//  5. Messages (sorted by name)
38	//  6. Enums (sorted by name)
39	//  7. Services (sorted by name)
40	//  8. Extensions (grouped by extendee, sorted by extendee+tag)
41	//
42	// The canonical order of elements in a message follows:
43	//  1. Options (sorted by name, standard options before custom options)
44	//  2. Fields and One-Ofs (sorted by tag; one-ofs interleaved based on the
45	//     minimum tag therein)
46	//  3. Nested Messages (sorted by name)
47	//  4. Nested Enums (sorted by name)
48	//  5. Extension ranges (sorted by starting tag number)
49	//  6. Nested Extensions (grouped by extendee, sorted by extendee+tag)
50	//  7. Reserved ranges (sorted by starting tag number)
51	//  8. Reserved names (sorted lexically)
52	//
53	// Methods are sorted within a service by name and appear after any service
54	// options (which are sorted by name, standard options before custom ones).
55	// Enum values are sorted within an enum, first by numeric value then by
56	// name, and also appear after any enum options.
57	//
58	// Options for fields, enum values, and extension ranges are sorted by name,
59	// standard options before custom ones.
60	SortElements bool
61
62	// The indentation used. Any characters other than spaces or tabs will be
63	// replaced with spaces. If unset/empty, two spaces will be used.
64	Indent string
65
66	// If true, detached comments (between elements) will be ignored.
67	//
68	// Deprecated: Use OmitComments bitmask instead.
69	OmitDetachedComments bool
70
71	// A bitmask of comment types to omit. If unset, all comments will be
72	// included. Use CommentsAll to not print any comments.
73	OmitComments CommentType
74
75	// If true, trailing comments that typically appear on the same line as an
76	// element (option, field, enum value, method) will be printed on a separate
77	// line instead.
78	//
79	// So, with this set, you'll get output like so:
80	//
81	//    // leading comment for field
82	//    repeated string names = 1;
83	//    // trailing comment
84	//
85	// If left false, the printer will try to emit trailing comments on the same
86	// line instead:
87	//
88	//    // leading comment for field
89	//    repeated string names = 1; // trailing comment
90	//
91	// If the trailing comment has more than one line, it will automatically be
92	// forced to the next line. Also, elements that end with "}" instead of ";"
93	// will have trailing comments rendered on the subsequent line.
94	TrailingCommentsOnSeparateLine bool
95
96	// If true, the printed output will eschew any blank lines, which otherwise
97	// appear between descriptor elements and comment blocks. Note that if
98	// detached comments are being printed, this will cause them to be merged
99	// into the subsequent leading comments. Similarly, any element trailing
100	// comments will be merged into the subsequent leading comments.
101	Compact bool
102
103	// If true, all references to messages, extensions, and enums (such as in
104	// options, field types, and method request and response types) will be
105	// fully-qualified. When left unset, the referenced elements will contain
106	// only as much qualifier as is required.
107	//
108	// For example, if a message is in the same package as the reference, the
109	// simple name can be used. If a message shares some context with the
110	// reference, only the unshared context needs to be included. For example:
111	//
112	//  message Foo {
113	//    message Bar {
114	//      enum Baz {
115	//        ZERO = 0;
116	//        ONE = 1;
117	//      }
118	//    }
119	//
120	//    // This field shares some context as the enum it references: they are
121	//    // both inside of the namespace Foo:
122	//    //    field is "Foo.my_baz"
123	//    //     enum is "Foo.Bar.Baz"
124	//    // So we only need to qualify the reference with the context that they
125	//    // do NOT have in common:
126	//    Bar.Baz my_baz = 1;
127	//  }
128	//
129	// When printing fully-qualified names, they will be preceded by a dot, to
130	// avoid any ambiguity that they might be relative vs. fully-qualified.
131	ForceFullyQualifiedNames bool
132}
133
134// CommentType is a kind of comments in a proto source file. This can be used
135// as a bitmask.
136type CommentType int
137
138const (
139	// CommentsDetached refers to comments that are not "attached" to any
140	// source element. They are attributed to the subsequent element in the
141	// file as "detached" comments.
142	CommentsDetached CommentType = 1 << iota
143	// CommentsTrailing refers to a comment block immediately following an
144	// element in the source file. If another element immediately follows
145	// the trailing comment, it is instead considered a leading comment for
146	// that subsequent element.
147	CommentsTrailing
148	// CommentsLeading refers to a comment block immediately preceding an
149	// element in the source file. For high-level elements (those that have
150	// their own descriptor), these are used as doc comments for that element.
151	CommentsLeading
152	// CommentsTokens refers to any comments (leading, trailing, or detached)
153	// on low-level elements in the file. "High-level" elements have their own
154	// descriptors, e.g. messages, enums, fields, services, and methods. But
155	// comments can appear anywhere (such as around identifiers and keywords,
156	// sprinkled inside the declarations of a high-level element). This class
157	// of comments are for those extra comments sprinkled into the file.
158	CommentsTokens
159
160	// CommentsNonDoc refers to comments that are *not* doc comments. This is a
161	// bitwise union of everything other than CommentsLeading. If you configure
162	// a printer to omit this, only doc comments on descriptor elements will be
163	// included in the printed output.
164	CommentsNonDoc = CommentsDetached | CommentsTrailing | CommentsTokens
165	// CommentsAll indicates all kinds of comments. If you configure a printer
166	// to omit this, no comments will appear in the printed output, even if the
167	// input descriptors had source info and comments.
168	CommentsAll = -1
169)
170
171// PrintProtoFiles prints all of the given file descriptors. The given open
172// function is given a file name and is responsible for creating the outputs and
173// returning the corresponding writer.
174func (p *Printer) PrintProtoFiles(fds []*desc.FileDescriptor, open func(name string) (io.WriteCloser, error)) error {
175	for _, fd := range fds {
176		w, err := open(fd.GetName())
177		if err != nil {
178			return fmt.Errorf("failed to open %s: %v", fd.GetName(), err)
179		}
180		err = func() error {
181			defer w.Close()
182			return p.PrintProtoFile(fd, w)
183		}()
184		if err != nil {
185			return fmt.Errorf("failed to write %s: %v", fd.GetName(), err)
186		}
187	}
188	return nil
189}
190
191// PrintProtosToFileSystem prints all of the given file descriptors to files in
192// the given directory. If file names in the given descriptors include path
193// information, they will be relative to the given root.
194func (p *Printer) PrintProtosToFileSystem(fds []*desc.FileDescriptor, rootDir string) error {
195	return p.PrintProtoFiles(fds, func(name string) (io.WriteCloser, error) {
196		fullPath := filepath.Join(rootDir, name)
197		dir := filepath.Dir(fullPath)
198		if err := os.MkdirAll(dir, os.ModePerm); err != nil {
199			return nil, err
200		}
201		return os.OpenFile(fullPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
202	})
203}
204
205// pkg represents a package name
206type pkg string
207
208// imp represents an imported file name
209type imp string
210
211// ident represents an identifier
212type ident string
213
214// option represents a resolved descriptor option
215type option struct {
216	name string
217	val  interface{}
218}
219
220// reservedRange represents a reserved range from a message or enum
221type reservedRange struct {
222	start, end int32
223}
224
225// PrintProtoFile prints the given single file descriptor to the given writer.
226func (p *Printer) PrintProtoFile(fd *desc.FileDescriptor, out io.Writer) error {
227	return p.printProto(fd, out)
228}
229
230// PrintProto prints the given descriptor and returns the resulting string. This
231// can be used to print proto files, but it can also be used to get the proto
232// "source form" for any kind of descriptor, which can be a more user-friendly
233// way to present descriptors that are intended for human consumption.
234func (p *Printer) PrintProtoToString(dsc desc.Descriptor) (string, error) {
235	var buf bytes.Buffer
236	if err := p.printProto(dsc, &buf); err != nil {
237		return "", err
238	}
239	return buf.String(), nil
240}
241
242func (p *Printer) printProto(dsc desc.Descriptor, out io.Writer) error {
243	w := newWriter(out)
244
245	if p.Indent == "" {
246		// default indent to two spaces
247		p.Indent = "  "
248	} else {
249		// indent must be all spaces or tabs, so convert other chars to spaces
250		ind := make([]rune, 0, len(p.Indent))
251		for _, r := range p.Indent {
252			if r == '\t' {
253				ind = append(ind, r)
254			} else {
255				ind = append(ind, ' ')
256			}
257		}
258		p.Indent = string(ind)
259	}
260	if p.OmitDetachedComments {
261		p.OmitComments |= CommentsDetached
262	}
263
264	er := dynamic.ExtensionRegistry{}
265	er.AddExtensionsFromFileRecursively(dsc.GetFile())
266	mf := dynamic.NewMessageFactoryWithExtensionRegistry(&er)
267	fdp := dsc.GetFile().AsFileDescriptorProto()
268	sourceInfo := internal.CreateSourceInfoMap(fdp)
269	extendOptionLocations(sourceInfo, fdp.GetSourceCodeInfo().GetLocation())
270
271	path := findElement(dsc)
272	switch d := dsc.(type) {
273	case *desc.FileDescriptor:
274		p.printFile(d, mf, w, sourceInfo)
275	case *desc.MessageDescriptor:
276		p.printMessage(d, mf, w, sourceInfo, path, 0)
277	case *desc.FieldDescriptor:
278		var scope string
279		if md, ok := d.GetParent().(*desc.MessageDescriptor); ok {
280			scope = md.GetFullyQualifiedName()
281		} else {
282			scope = d.GetFile().GetPackage()
283		}
284		if d.IsExtension() {
285			fmt.Fprint(w, "extend ")
286			extNameSi := sourceInfo.Get(append(path, internal.Field_extendeeTag))
287			p.printElementString(extNameSi, w, 0, p.qualifyName(d.GetFile().GetPackage(), scope, d.GetOwner().GetFullyQualifiedName()))
288			fmt.Fprintln(w, "{")
289
290			p.printField(d, mf, w, sourceInfo, path, scope, 1)
291
292			fmt.Fprintln(w, "}")
293		} else {
294			p.printField(d, mf, w, sourceInfo, path, scope, 0)
295		}
296	case *desc.OneOfDescriptor:
297		md := d.GetOwner()
298		elements := elementAddrs{dsc: md}
299		for i := range md.GetFields() {
300			elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_fieldsTag, elementIndex: i})
301		}
302		p.printOneOf(d, elements, 0, mf, w, sourceInfo, path[:len(path)-1], 0, path[len(path)-1])
303	case *desc.EnumDescriptor:
304		p.printEnum(d, mf, w, sourceInfo, path, 0)
305	case *desc.EnumValueDescriptor:
306		p.printEnumValue(d, mf, w, sourceInfo, path, 0)
307	case *desc.ServiceDescriptor:
308		p.printService(d, mf, w, sourceInfo, path, 0)
309	case *desc.MethodDescriptor:
310		p.printMethod(d, mf, w, sourceInfo, path, 0)
311	}
312
313	return w.err
314}
315
316func findElement(dsc desc.Descriptor) []int32 {
317	if dsc.GetParent() == nil {
318		return nil
319	}
320	path := findElement(dsc.GetParent())
321	switch d := dsc.(type) {
322	case *desc.MessageDescriptor:
323		if pm, ok := d.GetParent().(*desc.MessageDescriptor); ok {
324			return append(path, internal.Message_nestedMessagesTag, getMessageIndex(d, pm.GetNestedMessageTypes()))
325		}
326		return append(path, internal.File_messagesTag, getMessageIndex(d, d.GetFile().GetMessageTypes()))
327
328	case *desc.FieldDescriptor:
329		if d.IsExtension() {
330			if pm, ok := d.GetParent().(*desc.MessageDescriptor); ok {
331				return append(path, internal.Message_extensionsTag, getFieldIndex(d, pm.GetNestedExtensions()))
332			}
333			return append(path, internal.File_extensionsTag, getFieldIndex(d, d.GetFile().GetExtensions()))
334		}
335		return append(path, internal.Message_fieldsTag, getFieldIndex(d, d.GetOwner().GetFields()))
336
337	case *desc.OneOfDescriptor:
338		return append(path, internal.Message_oneOfsTag, getOneOfIndex(d, d.GetOwner().GetOneOfs()))
339
340	case *desc.EnumDescriptor:
341		if pm, ok := d.GetParent().(*desc.MessageDescriptor); ok {
342			return append(path, internal.Message_enumsTag, getEnumIndex(d, pm.GetNestedEnumTypes()))
343		}
344		return append(path, internal.File_enumsTag, getEnumIndex(d, d.GetFile().GetEnumTypes()))
345
346	case *desc.EnumValueDescriptor:
347		return append(path, internal.Enum_valuesTag, getEnumValueIndex(d, d.GetEnum().GetValues()))
348
349	case *desc.ServiceDescriptor:
350		return append(path, internal.File_servicesTag, getServiceIndex(d, d.GetFile().GetServices()))
351
352	case *desc.MethodDescriptor:
353		return append(path, internal.Service_methodsTag, getMethodIndex(d, d.GetService().GetMethods()))
354
355	default:
356		panic(fmt.Sprintf("unexpected descriptor type: %T", dsc))
357	}
358}
359
360func getMessageIndex(md *desc.MessageDescriptor, list []*desc.MessageDescriptor) int32 {
361	for i := range list {
362		if md == list[i] {
363			return int32(i)
364		}
365	}
366	panic(fmt.Sprintf("unable to determine index of message %s", md.GetFullyQualifiedName()))
367}
368
369func getFieldIndex(fd *desc.FieldDescriptor, list []*desc.FieldDescriptor) int32 {
370	for i := range list {
371		if fd == list[i] {
372			return int32(i)
373		}
374	}
375	panic(fmt.Sprintf("unable to determine index of field %s", fd.GetFullyQualifiedName()))
376}
377
378func getOneOfIndex(ood *desc.OneOfDescriptor, list []*desc.OneOfDescriptor) int32 {
379	for i := range list {
380		if ood == list[i] {
381			return int32(i)
382		}
383	}
384	panic(fmt.Sprintf("unable to determine index of oneof %s", ood.GetFullyQualifiedName()))
385}
386
387func getEnumIndex(ed *desc.EnumDescriptor, list []*desc.EnumDescriptor) int32 {
388	for i := range list {
389		if ed == list[i] {
390			return int32(i)
391		}
392	}
393	panic(fmt.Sprintf("unable to determine index of enum %s", ed.GetFullyQualifiedName()))
394}
395
396func getEnumValueIndex(evd *desc.EnumValueDescriptor, list []*desc.EnumValueDescriptor) int32 {
397	for i := range list {
398		if evd == list[i] {
399			return int32(i)
400		}
401	}
402	panic(fmt.Sprintf("unable to determine index of enum value %s", evd.GetFullyQualifiedName()))
403}
404
405func getServiceIndex(sd *desc.ServiceDescriptor, list []*desc.ServiceDescriptor) int32 {
406	for i := range list {
407		if sd == list[i] {
408			return int32(i)
409		}
410	}
411	panic(fmt.Sprintf("unable to determine index of service %s", sd.GetFullyQualifiedName()))
412}
413
414func getMethodIndex(mtd *desc.MethodDescriptor, list []*desc.MethodDescriptor) int32 {
415	for i := range list {
416		if mtd == list[i] {
417			return int32(i)
418		}
419	}
420	panic(fmt.Sprintf("unable to determine index of method %s", mtd.GetFullyQualifiedName()))
421}
422
423func (p *Printer) newLine(w io.Writer) {
424	if !p.Compact {
425		fmt.Fprintln(w)
426	}
427}
428
429func (p *Printer) printFile(fd *desc.FileDescriptor, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap) {
430	opts, err := p.extractOptions(fd, fd.GetOptions(), mf)
431	if err != nil {
432		return
433	}
434
435	fdp := fd.AsFileDescriptorProto()
436	path := make([]int32, 1)
437
438	path[0] = internal.File_packageTag
439	sourceInfo.PutIfAbsent(append(path, 0), sourceInfo.Get(path))
440
441	path[0] = internal.File_syntaxTag
442	si := sourceInfo.Get(path)
443	p.printElement(false, si, w, 0, func(w *writer) {
444		syn := fdp.GetSyntax()
445		if syn == "" {
446			syn = "proto2"
447		}
448		fmt.Fprintf(w, "syntax = %q;", syn)
449	})
450	p.newLine(w)
451
452	skip := map[interface{}]bool{}
453
454	elements := elementAddrs{dsc: fd, opts: opts}
455	if fdp.Package != nil {
456		elements.addrs = append(elements.addrs, elementAddr{elementType: internal.File_packageTag, elementIndex: 0, order: -3})
457	}
458	for i := range fdp.GetDependency() {
459		elements.addrs = append(elements.addrs, elementAddr{elementType: internal.File_dependencyTag, elementIndex: i, order: -2})
460	}
461	elements.addrs = append(elements.addrs, optionsAsElementAddrs(internal.File_optionsTag, -1, opts)...)
462	for i := range fd.GetMessageTypes() {
463		elements.addrs = append(elements.addrs, elementAddr{elementType: internal.File_messagesTag, elementIndex: i})
464	}
465	for i := range fd.GetEnumTypes() {
466		elements.addrs = append(elements.addrs, elementAddr{elementType: internal.File_enumsTag, elementIndex: i})
467	}
468	for i := range fd.GetServices() {
469		elements.addrs = append(elements.addrs, elementAddr{elementType: internal.File_servicesTag, elementIndex: i})
470	}
471	exts := p.computeExtensions(sourceInfo, fd.GetExtensions(), []int32{internal.File_extensionsTag})
472	for i, extd := range fd.GetExtensions() {
473		if extd.GetType() == descriptor.FieldDescriptorProto_TYPE_GROUP {
474			// we don't emit nested messages for groups since
475			// they get special treatment
476			skip[extd.GetMessageType()] = true
477		}
478		elements.addrs = append(elements.addrs, elementAddr{elementType: internal.File_extensionsTag, elementIndex: i})
479	}
480
481	p.sort(elements, sourceInfo, nil)
482
483	pkgName := fd.GetPackage()
484
485	for i, el := range elements.addrs {
486		d := elements.at(el)
487
488		// skip[d] will panic if d is a slice (which it could be for []option),
489		// so just ignore it since we don't try to skip options
490		if reflect.TypeOf(d).Kind() != reflect.Slice && skip[d] {
491			// skip this element
492			continue
493		}
494
495		if i > 0 {
496			p.newLine(w)
497		}
498
499		path = []int32{el.elementType, int32(el.elementIndex)}
500
501		switch d := d.(type) {
502		case pkg:
503			si := sourceInfo.Get(path)
504			p.printElement(false, si, w, 0, func(w *writer) {
505				fmt.Fprintf(w, "package %s;", d)
506			})
507		case imp:
508			si := sourceInfo.Get(path)
509			var modifier string
510			for _, idx := range fdp.PublicDependency {
511				if fdp.Dependency[idx] == string(d) {
512					modifier = "public "
513					break
514				}
515			}
516			if modifier == "" {
517				for _, idx := range fdp.WeakDependency {
518					if fdp.Dependency[idx] == string(d) {
519						modifier = "weak "
520						break
521					}
522				}
523			}
524			p.printElement(false, si, w, 0, func(w *writer) {
525				fmt.Fprintf(w, "import %s%q;", modifier, d)
526			})
527		case []option:
528			p.printOptionsLong(d, w, sourceInfo, path, 0)
529		case *desc.MessageDescriptor:
530			p.printMessage(d, mf, w, sourceInfo, path, 0)
531		case *desc.EnumDescriptor:
532			p.printEnum(d, mf, w, sourceInfo, path, 0)
533		case *desc.ServiceDescriptor:
534			p.printService(d, mf, w, sourceInfo, path, 0)
535		case *desc.FieldDescriptor:
536			extDecl := exts[d]
537			p.printExtensions(extDecl, exts, elements, i, mf, w, sourceInfo, nil, internal.File_extensionsTag, pkgName, pkgName, 0)
538			// we printed all extensions in the group, so we can skip the others
539			for _, fld := range extDecl.fields {
540				skip[fld] = true
541			}
542		}
543	}
544}
545
546func findExtSi(fieldSi *descriptor.SourceCodeInfo_Location, extSis []*descriptor.SourceCodeInfo_Location) *descriptor.SourceCodeInfo_Location {
547	if len(fieldSi.GetSpan()) == 0 {
548		return nil
549	}
550	for _, extSi := range extSis {
551		if isSpanWithin(fieldSi.Span, extSi.Span) {
552			return extSi
553		}
554	}
555	return nil
556}
557
558func isSpanWithin(span, enclosing []int32) bool {
559	start := enclosing[0]
560	var end int32
561	if len(enclosing) == 3 {
562		end = enclosing[0]
563	} else {
564		end = enclosing[2]
565	}
566	if span[0] < start || span[0] > end {
567		return false
568	}
569
570	if span[0] == start {
571		return span[1] >= enclosing[1]
572	} else if span[0] == end {
573		return span[1] <= enclosing[len(enclosing)-1]
574	}
575	return true
576}
577
578type extensionDecl struct {
579	extendee   string
580	sourceInfo *descriptor.SourceCodeInfo_Location
581	fields     []*desc.FieldDescriptor
582}
583
584type extensions map[*desc.FieldDescriptor]*extensionDecl
585
586func (p *Printer) computeExtensions(sourceInfo internal.SourceInfoMap, exts []*desc.FieldDescriptor, path []int32) extensions {
587	extsMap := map[string]map[*descriptor.SourceCodeInfo_Location]*extensionDecl{}
588	extSis := sourceInfo.GetAll(path)
589	for _, extd := range exts {
590		name := extd.GetOwner().GetFullyQualifiedName()
591		extSi := findExtSi(extd.GetSourceInfo(), extSis)
592		extsBySi := extsMap[name]
593		if extsBySi == nil {
594			extsBySi = map[*descriptor.SourceCodeInfo_Location]*extensionDecl{}
595			extsMap[name] = extsBySi
596		}
597		extDecl := extsBySi[extSi]
598		if extDecl == nil {
599			extDecl = &extensionDecl{
600				sourceInfo: extSi,
601				extendee:   name,
602			}
603			extsBySi[extSi] = extDecl
604		}
605		extDecl.fields = append(extDecl.fields, extd)
606	}
607
608	ret := extensions{}
609	for _, extsBySi := range extsMap {
610		for _, extDecl := range extsBySi {
611			for _, extd := range extDecl.fields {
612				ret[extd] = extDecl
613			}
614		}
615	}
616	return ret
617}
618
619func (p *Printer) sort(elements elementAddrs, sourceInfo internal.SourceInfoMap, path []int32) {
620	if p.SortElements {
621		// canonical sorted order
622		sort.Stable(elements)
623	} else {
624		// use source order (per location information in SourceCodeInfo); or
625		// if that isn't present use declaration order, but grouped by type
626		sort.Stable(elementSrcOrder{
627			elementAddrs: elements,
628			sourceInfo:   sourceInfo,
629			prefix:       path,
630		})
631	}
632}
633
634func (p *Printer) qualifyName(pkg, scope string, fqn string) string {
635	if p.ForceFullyQualifiedNames {
636		// forcing fully-qualified names; make sure to include preceding dot
637		if fqn[0] == '.' {
638			return fqn
639		}
640		return fmt.Sprintf(".%s", fqn)
641	}
642
643	// compute relative name (so no leading dot)
644	if fqn[0] == '.' {
645		fqn = fqn[1:]
646	}
647	if len(scope) > 0 && scope[len(scope)-1] != '.' {
648		scope = scope + "."
649	}
650	for scope != "" {
651		if strings.HasPrefix(fqn, scope) {
652			return fqn[len(scope):]
653		}
654		if scope == pkg+"." {
655			break
656		}
657		pos := strings.LastIndex(scope[:len(scope)-1], ".")
658		scope = scope[:pos+1]
659	}
660	return fqn
661}
662
663func (p *Printer) typeString(fld *desc.FieldDescriptor, scope string) string {
664	if fld.IsMap() {
665		return fmt.Sprintf("map<%s, %s>", p.typeString(fld.GetMapKeyType(), scope), p.typeString(fld.GetMapValueType(), scope))
666	}
667	switch fld.GetType() {
668	case descriptor.FieldDescriptorProto_TYPE_INT32:
669		return "int32"
670	case descriptor.FieldDescriptorProto_TYPE_INT64:
671		return "int64"
672	case descriptor.FieldDescriptorProto_TYPE_UINT32:
673		return "uint32"
674	case descriptor.FieldDescriptorProto_TYPE_UINT64:
675		return "uint64"
676	case descriptor.FieldDescriptorProto_TYPE_SINT32:
677		return "sint32"
678	case descriptor.FieldDescriptorProto_TYPE_SINT64:
679		return "sint64"
680	case descriptor.FieldDescriptorProto_TYPE_FIXED32:
681		return "fixed32"
682	case descriptor.FieldDescriptorProto_TYPE_FIXED64:
683		return "fixed64"
684	case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
685		return "sfixed32"
686	case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
687		return "sfixed64"
688	case descriptor.FieldDescriptorProto_TYPE_FLOAT:
689		return "float"
690	case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
691		return "double"
692	case descriptor.FieldDescriptorProto_TYPE_BOOL:
693		return "bool"
694	case descriptor.FieldDescriptorProto_TYPE_STRING:
695		return "string"
696	case descriptor.FieldDescriptorProto_TYPE_BYTES:
697		return "bytes"
698	case descriptor.FieldDescriptorProto_TYPE_ENUM:
699		return p.qualifyName(fld.GetFile().GetPackage(), scope, fld.GetEnumType().GetFullyQualifiedName())
700	case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
701		return p.qualifyName(fld.GetFile().GetPackage(), scope, fld.GetMessageType().GetFullyQualifiedName())
702	case descriptor.FieldDescriptorProto_TYPE_GROUP:
703		return fld.GetMessageType().GetName()
704	}
705	panic(fmt.Sprintf("invalid type: %v", fld.GetType()))
706}
707
708func (p *Printer) printMessage(md *desc.MessageDescriptor, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) {
709	si := sourceInfo.Get(path)
710	p.printElement(true, si, w, indent, func(w *writer) {
711		p.indent(w, indent)
712
713		fmt.Fprint(w, "message ")
714		nameSi := sourceInfo.Get(append(path, internal.Message_nameTag))
715		p.printElementString(nameSi, w, indent, md.GetName())
716		fmt.Fprintln(w, "{")
717
718		p.printMessageBody(md, mf, w, sourceInfo, path, indent+1)
719		p.indent(w, indent)
720		fmt.Fprintln(w, "}")
721	})
722}
723
724func (p *Printer) printMessageBody(md *desc.MessageDescriptor, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) {
725	opts, err := p.extractOptions(md, md.GetOptions(), mf)
726	if err != nil {
727		if w.err == nil {
728			w.err = err
729		}
730		return
731	}
732
733	skip := map[interface{}]bool{}
734	maxTag := internal.GetMaxTag(md.GetMessageOptions().GetMessageSetWireFormat())
735
736	elements := elementAddrs{dsc: md, opts: opts}
737	elements.addrs = append(elements.addrs, optionsAsElementAddrs(internal.Message_optionsTag, -1, opts)...)
738	for i := range md.AsDescriptorProto().GetReservedRange() {
739		elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_reservedRangeTag, elementIndex: i})
740	}
741	for i := range md.AsDescriptorProto().GetReservedName() {
742		elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_reservedNameTag, elementIndex: i})
743	}
744	for i := range md.AsDescriptorProto().GetExtensionRange() {
745		elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_extensionRangeTag, elementIndex: i})
746	}
747	for i, fld := range md.GetFields() {
748		if fld.IsMap() || fld.GetType() == descriptor.FieldDescriptorProto_TYPE_GROUP {
749			// we don't emit nested messages for map types or groups since
750			// they get special treatment
751			skip[fld.GetMessageType()] = true
752		}
753		elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_fieldsTag, elementIndex: i})
754	}
755	for i := range md.GetNestedMessageTypes() {
756		elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_nestedMessagesTag, elementIndex: i})
757	}
758	for i := range md.GetNestedEnumTypes() {
759		elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_enumsTag, elementIndex: i})
760	}
761	exts := p.computeExtensions(sourceInfo, md.GetNestedExtensions(), append(path, internal.Message_extensionsTag))
762	for i, extd := range md.GetNestedExtensions() {
763		if extd.GetType() == descriptor.FieldDescriptorProto_TYPE_GROUP {
764			// we don't emit nested messages for groups since
765			// they get special treatment
766			skip[extd.GetMessageType()] = true
767		}
768		elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_extensionsTag, elementIndex: i})
769	}
770
771	p.sort(elements, sourceInfo, path)
772
773	pkg := md.GetFile().GetPackage()
774	scope := md.GetFullyQualifiedName()
775
776	for i, el := range elements.addrs {
777		d := elements.at(el)
778
779		// skip[d] will panic if d is a slice (which it could be for []option),
780		// so just ignore it since we don't try to skip options
781		if reflect.TypeOf(d).Kind() != reflect.Slice && skip[d] {
782			// skip this element
783			continue
784		}
785
786		if i > 0 {
787			p.newLine(w)
788		}
789
790		childPath := append(path, el.elementType, int32(el.elementIndex))
791
792		switch d := d.(type) {
793		case []option:
794			p.printOptionsLong(d, w, sourceInfo, childPath, indent)
795		case *desc.FieldDescriptor:
796			if d.IsExtension() {
797				extDecl := exts[d]
798				p.printExtensions(extDecl, exts, elements, i, mf, w, sourceInfo, path, internal.Message_extensionsTag, pkg, scope, indent)
799				// we printed all extensions in the group, so we can skip the others
800				for _, fld := range extDecl.fields {
801					skip[fld] = true
802				}
803			} else {
804				ood := d.GetOneOf()
805				if ood == nil || ood.IsSynthetic() {
806					p.printField(d, mf, w, sourceInfo, childPath, scope, indent)
807				} else {
808					// print the one-of, including all of its fields
809					p.printOneOf(ood, elements, i, mf, w, sourceInfo, path, indent, d.AsFieldDescriptorProto().GetOneofIndex())
810					for _, fld := range ood.GetChoices() {
811						skip[fld] = true
812					}
813				}
814			}
815		case *desc.MessageDescriptor:
816			p.printMessage(d, mf, w, sourceInfo, childPath, indent)
817		case *desc.EnumDescriptor:
818			p.printEnum(d, mf, w, sourceInfo, childPath, indent)
819		case *descriptor.DescriptorProto_ExtensionRange:
820			// collapse ranges into a single "extensions" block
821			ranges := []*descriptor.DescriptorProto_ExtensionRange{d}
822			addrs := []elementAddr{el}
823			for idx := i + 1; idx < len(elements.addrs); idx++ {
824				elnext := elements.addrs[idx]
825				if elnext.elementType != el.elementType {
826					break
827				}
828				extr := elements.at(elnext).(*descriptor.DescriptorProto_ExtensionRange)
829				if !areEqual(d.Options, extr.Options, mf) {
830					break
831				}
832				ranges = append(ranges, extr)
833				addrs = append(addrs, elnext)
834				skip[extr] = true
835			}
836			p.printExtensionRanges(md, ranges, maxTag, addrs, mf, w, sourceInfo, path, indent)
837		case reservedRange:
838			// collapse reserved ranges into a single "reserved" block
839			ranges := []reservedRange{d}
840			addrs := []elementAddr{el}
841			for idx := i + 1; idx < len(elements.addrs); idx++ {
842				elnext := elements.addrs[idx]
843				if elnext.elementType != el.elementType {
844					break
845				}
846				rr := elements.at(elnext).(reservedRange)
847				ranges = append(ranges, rr)
848				addrs = append(addrs, elnext)
849				skip[rr] = true
850			}
851			p.printReservedRanges(ranges, maxTag, addrs, w, sourceInfo, path, indent)
852		case string: // reserved name
853			// collapse reserved names into a single "reserved" block
854			names := []string{d}
855			addrs := []elementAddr{el}
856			for idx := i + 1; idx < len(elements.addrs); idx++ {
857				elnext := elements.addrs[idx]
858				if elnext.elementType != el.elementType {
859					break
860				}
861				rn := elements.at(elnext).(string)
862				names = append(names, rn)
863				addrs = append(addrs, elnext)
864				skip[rn] = true
865			}
866			p.printReservedNames(names, addrs, w, sourceInfo, path, indent)
867		}
868	}
869}
870
871func areEqual(a, b proto.Message, mf *dynamic.MessageFactory) bool {
872	// proto.Equal doesn't handle unknown extensions very well :(
873	// so we convert to a dynamic message (which should know about all extensions via
874	// extension registry) and then compare
875	return dynamic.MessagesEqual(asDynamicIfPossible(a, mf), asDynamicIfPossible(b, mf))
876}
877
878func asDynamicIfPossible(msg proto.Message, mf *dynamic.MessageFactory) proto.Message {
879	if dm, ok := msg.(*dynamic.Message); ok {
880		return dm
881	} else {
882		md, err := desc.LoadMessageDescriptorForMessage(msg)
883		if err == nil {
884			dm := mf.NewDynamicMessage(md)
885			if dm.ConvertFrom(msg) == nil {
886				return dm
887			}
888		}
889	}
890	return msg
891}
892
893func (p *Printer) printField(fld *desc.FieldDescriptor, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap, path []int32, scope string, indent int) {
894	var groupPath []int32
895	var si *descriptor.SourceCodeInfo_Location
896
897	group := isGroup(fld)
898
899	if group {
900		// compute path to group message type
901		groupPath = make([]int32, len(path)-2)
902		copy(groupPath, path)
903
904		var candidates []*desc.MessageDescriptor
905		var parentTag int32
906		switch parent := fld.GetParent().(type) {
907		case *desc.MessageDescriptor:
908			// group in a message
909			candidates = parent.GetNestedMessageTypes()
910			parentTag = internal.Message_nestedMessagesTag
911		case *desc.FileDescriptor:
912			// group that is a top-level extension
913			candidates = parent.GetMessageTypes()
914			parentTag = internal.File_messagesTag
915		}
916
917		var groupMsgIndex int32
918		for i, nmd := range candidates {
919			if nmd == fld.GetMessageType() {
920				// found it
921				groupMsgIndex = int32(i)
922				break
923			}
924		}
925		groupPath = append(groupPath, parentTag, groupMsgIndex)
926
927		// the group message is where the field's comments and position are stored
928		si = sourceInfo.Get(groupPath)
929	} else {
930		si = sourceInfo.Get(path)
931	}
932
933	p.printElement(true, si, w, indent, func(w *writer) {
934		p.indent(w, indent)
935		if shouldEmitLabel(fld) {
936			locSi := sourceInfo.Get(append(path, internal.Field_labelTag))
937			p.printElementString(locSi, w, indent, labelString(fld.GetLabel()))
938		}
939
940		if group {
941			fmt.Fprint(w, "group ")
942		}
943
944		typeSi := sourceInfo.Get(append(path, internal.Field_typeTag))
945		p.printElementString(typeSi, w, indent, p.typeString(fld, scope))
946
947		if !group {
948			nameSi := sourceInfo.Get(append(path, internal.Field_nameTag))
949			p.printElementString(nameSi, w, indent, fld.GetName())
950		}
951
952		fmt.Fprint(w, "= ")
953		numSi := sourceInfo.Get(append(path, internal.Field_numberTag))
954		p.printElementString(numSi, w, indent, fmt.Sprintf("%d", fld.GetNumber()))
955
956		opts, err := p.extractOptions(fld, fld.GetOptions(), mf)
957		if err != nil {
958			if w.err == nil {
959				w.err = err
960			}
961			return
962		}
963
964		// we use negative values for "extras" keys so they can't collide
965		// with legit option tags
966
967		if !fld.GetFile().IsProto3() && fld.AsFieldDescriptorProto().DefaultValue != nil {
968			defVal := fld.GetDefaultValue()
969			if fld.GetEnumType() != nil {
970				defVal = fld.GetEnumType().FindValueByNumber(defVal.(int32))
971			}
972			opts[-internal.Field_defaultTag] = []option{{name: "default", val: defVal}}
973		}
974
975		jsn := fld.AsFieldDescriptorProto().GetJsonName()
976		if jsn != "" && jsn != internal.JsonName(fld.GetName()) {
977			opts[-internal.Field_jsonNameTag] = []option{{name: "json_name", val: jsn}}
978		}
979
980		elements := elementAddrs{dsc: fld, opts: opts}
981		elements.addrs = optionsAsElementAddrs(internal.Field_optionsTag, 0, opts)
982		p.sort(elements, sourceInfo, path)
983		p.printOptionElementsShort(elements, w, sourceInfo, path, indent)
984
985		if group {
986			fmt.Fprintln(w, "{")
987			p.printMessageBody(fld.GetMessageType(), mf, w, sourceInfo, groupPath, indent+1)
988
989			p.indent(w, indent)
990			fmt.Fprintln(w, "}")
991
992		} else {
993			fmt.Fprint(w, ";")
994		}
995	})
996}
997
998func shouldEmitLabel(fld *desc.FieldDescriptor) bool {
999	return fld.IsProto3Optional() ||
1000		(!fld.IsMap() && fld.GetOneOf() == nil &&
1001			(fld.GetLabel() != descriptor.FieldDescriptorProto_LABEL_OPTIONAL || !fld.GetFile().IsProto3()))
1002}
1003
1004func labelString(lbl descriptor.FieldDescriptorProto_Label) string {
1005	switch lbl {
1006	case descriptor.FieldDescriptorProto_LABEL_OPTIONAL:
1007		return "optional"
1008	case descriptor.FieldDescriptorProto_LABEL_REQUIRED:
1009		return "required"
1010	case descriptor.FieldDescriptorProto_LABEL_REPEATED:
1011		return "repeated"
1012	}
1013	panic(fmt.Sprintf("invalid label: %v", lbl))
1014}
1015
1016func isGroup(fld *desc.FieldDescriptor) bool {
1017	return fld.GetType() == descriptor.FieldDescriptorProto_TYPE_GROUP
1018}
1019
1020func (p *Printer) printOneOf(ood *desc.OneOfDescriptor, parentElements elementAddrs, startFieldIndex int, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap, parentPath []int32, indent int, ooIndex int32) {
1021	oopath := append(parentPath, internal.Message_oneOfsTag, ooIndex)
1022	oosi := sourceInfo.Get(oopath)
1023	p.printElement(true, oosi, w, indent, func(w *writer) {
1024		p.indent(w, indent)
1025		fmt.Fprint(w, "oneof ")
1026		extNameSi := sourceInfo.Get(append(oopath, internal.OneOf_nameTag))
1027		p.printElementString(extNameSi, w, indent, ood.GetName())
1028		fmt.Fprintln(w, "{")
1029
1030		indent++
1031		opts, err := p.extractOptions(ood, ood.GetOptions(), mf)
1032		if err != nil {
1033			if w.err == nil {
1034				w.err = err
1035			}
1036			return
1037		}
1038
1039		elements := elementAddrs{dsc: ood, opts: opts}
1040		elements.addrs = append(elements.addrs, optionsAsElementAddrs(internal.OneOf_optionsTag, -1, opts)...)
1041
1042		count := len(ood.GetChoices())
1043		for idx := startFieldIndex; count > 0 && idx < len(parentElements.addrs); idx++ {
1044			el := parentElements.addrs[idx]
1045			if el.elementType != internal.Message_fieldsTag {
1046				continue
1047			}
1048			if parentElements.at(el).(*desc.FieldDescriptor).GetOneOf() == ood {
1049				// negative tag indicates that this element is actually a sibling, not a child
1050				elements.addrs = append(elements.addrs, elementAddr{elementType: -internal.Message_fieldsTag, elementIndex: el.elementIndex})
1051				count--
1052			}
1053		}
1054
1055		// the fields are already sorted, but we have to re-sort in order to
1056		// interleave the options (in the event that we are using file location
1057		// order and the option locations are interleaved with the fields)
1058		p.sort(elements, sourceInfo, oopath)
1059		scope := ood.GetOwner().GetFullyQualifiedName()
1060
1061		for i, el := range elements.addrs {
1062			if i > 0 {
1063				p.newLine(w)
1064			}
1065
1066			switch d := elements.at(el).(type) {
1067			case []option:
1068				childPath := append(oopath, el.elementType, int32(el.elementIndex))
1069				p.printOptionsLong(d, w, sourceInfo, childPath, indent)
1070			case *desc.FieldDescriptor:
1071				childPath := append(parentPath, -el.elementType, int32(el.elementIndex))
1072				p.printField(d, mf, w, sourceInfo, childPath, scope, indent)
1073			}
1074		}
1075
1076		p.indent(w, indent-1)
1077		fmt.Fprintln(w, "}")
1078	})
1079}
1080
1081func (p *Printer) printExtensions(exts *extensionDecl, allExts extensions, parentElements elementAddrs, startFieldIndex int, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap, parentPath []int32, extTag int32, pkg, scope string, indent int) {
1082	path := append(parentPath, extTag)
1083	p.printLeadingComments(exts.sourceInfo, w, indent)
1084	p.indent(w, indent)
1085	fmt.Fprint(w, "extend ")
1086	extNameSi := sourceInfo.Get(append(path, 0, internal.Field_extendeeTag))
1087	p.printElementString(extNameSi, w, indent, p.qualifyName(pkg, scope, exts.extendee))
1088	fmt.Fprintln(w, "{")
1089
1090	count := len(exts.fields)
1091	first := true
1092	for idx := startFieldIndex; count > 0 && idx < len(parentElements.addrs); idx++ {
1093		el := parentElements.addrs[idx]
1094		if el.elementType != extTag {
1095			continue
1096		}
1097		fld := parentElements.at(el).(*desc.FieldDescriptor)
1098		if allExts[fld] == exts {
1099			if first {
1100				first = false
1101			} else {
1102				p.newLine(w)
1103			}
1104			childPath := append(path, int32(el.elementIndex))
1105			p.printField(fld, mf, w, sourceInfo, childPath, scope, indent+1)
1106			count--
1107		}
1108	}
1109
1110	p.indent(w, indent)
1111	fmt.Fprintln(w, "}")
1112	p.printTrailingComments(exts.sourceInfo, w, indent)
1113	if indent >= 0 && !w.newline {
1114		// if we're not printing inline but element did not have trailing newline, add one now
1115		fmt.Fprintln(w)
1116	}
1117}
1118
1119func (p *Printer) printExtensionRanges(parent *desc.MessageDescriptor, ranges []*descriptor.DescriptorProto_ExtensionRange, maxTag int32, addrs []elementAddr, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap, parentPath []int32, indent int) {
1120	p.indent(w, indent)
1121	fmt.Fprint(w, "extensions ")
1122
1123	var opts *descriptor.ExtensionRangeOptions
1124	var elPath []int32
1125	first := true
1126	for i, extr := range ranges {
1127		if first {
1128			first = false
1129		} else {
1130			fmt.Fprint(w, ", ")
1131		}
1132		opts = extr.Options
1133		el := addrs[i]
1134		elPath = append(parentPath, el.elementType, int32(el.elementIndex))
1135		si := sourceInfo.Get(elPath)
1136		p.printElement(true, si, w, inline(indent), func(w *writer) {
1137			if extr.GetStart() == extr.GetEnd()-1 {
1138				fmt.Fprintf(w, "%d ", extr.GetStart())
1139			} else if extr.GetEnd()-1 == maxTag {
1140				fmt.Fprintf(w, "%d to max ", extr.GetStart())
1141			} else {
1142				fmt.Fprintf(w, "%d to %d ", extr.GetStart(), extr.GetEnd()-1)
1143			}
1144		})
1145	}
1146	dsc := extensionRange{owner: parent, extRange: ranges[0]}
1147	p.printOptionsShort(dsc, opts, mf, internal.ExtensionRange_optionsTag, w, sourceInfo, elPath, indent)
1148
1149	fmt.Fprintln(w, ";")
1150}
1151
1152func (p *Printer) printReservedRanges(ranges []reservedRange, maxVal int32, addrs []elementAddr, w *writer, sourceInfo internal.SourceInfoMap, parentPath []int32, indent int) {
1153	p.indent(w, indent)
1154	fmt.Fprint(w, "reserved ")
1155
1156	first := true
1157	for i, rr := range ranges {
1158		if first {
1159			first = false
1160		} else {
1161			fmt.Fprint(w, ", ")
1162		}
1163		el := addrs[i]
1164		si := sourceInfo.Get(append(parentPath, el.elementType, int32(el.elementIndex)))
1165		p.printElement(false, si, w, inline(indent), func(w *writer) {
1166			if rr.start == rr.end {
1167				fmt.Fprintf(w, "%d ", rr.start)
1168			} else if rr.end == maxVal {
1169				fmt.Fprintf(w, "%d to max ", rr.start)
1170			} else {
1171				fmt.Fprintf(w, "%d to %d ", rr.start, rr.end)
1172			}
1173		})
1174	}
1175
1176	fmt.Fprintln(w, ";")
1177}
1178
1179func (p *Printer) printReservedNames(names []string, addrs []elementAddr, w *writer, sourceInfo internal.SourceInfoMap, parentPath []int32, indent int) {
1180	p.indent(w, indent)
1181	fmt.Fprint(w, "reserved ")
1182
1183	first := true
1184	for i, name := range names {
1185		if first {
1186			first = false
1187		} else {
1188			fmt.Fprint(w, ", ")
1189		}
1190		el := addrs[i]
1191		si := sourceInfo.Get(append(parentPath, el.elementType, int32(el.elementIndex)))
1192		p.printElementString(si, w, indent, quotedString(name))
1193	}
1194
1195	fmt.Fprintln(w, ";")
1196}
1197
1198func (p *Printer) printEnum(ed *desc.EnumDescriptor, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) {
1199	si := sourceInfo.Get(path)
1200	p.printElement(true, si, w, indent, func(w *writer) {
1201		p.indent(w, indent)
1202
1203		fmt.Fprint(w, "enum ")
1204		nameSi := sourceInfo.Get(append(path, internal.Enum_nameTag))
1205		p.printElementString(nameSi, w, indent, ed.GetName())
1206		fmt.Fprintln(w, "{")
1207
1208		indent++
1209		opts, err := p.extractOptions(ed, ed.GetOptions(), mf)
1210		if err != nil {
1211			if w.err == nil {
1212				w.err = err
1213			}
1214			return
1215		}
1216
1217		skip := map[interface{}]bool{}
1218
1219		elements := elementAddrs{dsc: ed, opts: opts}
1220		elements.addrs = append(elements.addrs, optionsAsElementAddrs(internal.Enum_optionsTag, -1, opts)...)
1221		for i := range ed.GetValues() {
1222			elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Enum_valuesTag, elementIndex: i})
1223		}
1224		for i := range ed.AsEnumDescriptorProto().GetReservedRange() {
1225			elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Enum_reservedRangeTag, elementIndex: i})
1226		}
1227		for i := range ed.AsEnumDescriptorProto().GetReservedName() {
1228			elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Enum_reservedNameTag, elementIndex: i})
1229		}
1230
1231		p.sort(elements, sourceInfo, path)
1232
1233		for i, el := range elements.addrs {
1234			d := elements.at(el)
1235
1236			// skip[d] will panic if d is a slice (which it could be for []option),
1237			// so just ignore it since we don't try to skip options
1238			if reflect.TypeOf(d).Kind() != reflect.Slice && skip[d] {
1239				// skip this element
1240				continue
1241			}
1242
1243			if i > 0 {
1244				p.newLine(w)
1245			}
1246
1247			childPath := append(path, el.elementType, int32(el.elementIndex))
1248
1249			switch d := d.(type) {
1250			case []option:
1251				p.printOptionsLong(d, w, sourceInfo, childPath, indent)
1252			case *desc.EnumValueDescriptor:
1253				p.printEnumValue(d, mf, w, sourceInfo, childPath, indent)
1254			case reservedRange:
1255				// collapse reserved ranges into a single "reserved" block
1256				ranges := []reservedRange{d}
1257				addrs := []elementAddr{el}
1258				for idx := i + 1; idx < len(elements.addrs); idx++ {
1259					elnext := elements.addrs[idx]
1260					if elnext.elementType != el.elementType {
1261						break
1262					}
1263					rr := elements.at(elnext).(reservedRange)
1264					ranges = append(ranges, rr)
1265					addrs = append(addrs, elnext)
1266					skip[rr] = true
1267				}
1268				p.printReservedRanges(ranges, math.MaxInt32, addrs, w, sourceInfo, path, indent)
1269			case string: // reserved name
1270				// collapse reserved names into a single "reserved" block
1271				names := []string{d}
1272				addrs := []elementAddr{el}
1273				for idx := i + 1; idx < len(elements.addrs); idx++ {
1274					elnext := elements.addrs[idx]
1275					if elnext.elementType != el.elementType {
1276						break
1277					}
1278					rn := elements.at(elnext).(string)
1279					names = append(names, rn)
1280					addrs = append(addrs, elnext)
1281					skip[rn] = true
1282				}
1283				p.printReservedNames(names, addrs, w, sourceInfo, path, indent)
1284			}
1285		}
1286
1287		p.indent(w, indent-1)
1288		fmt.Fprintln(w, "}")
1289	})
1290}
1291
1292func (p *Printer) printEnumValue(evd *desc.EnumValueDescriptor, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) {
1293	si := sourceInfo.Get(path)
1294	p.printElement(true, si, w, indent, func(w *writer) {
1295		p.indent(w, indent)
1296
1297		nameSi := sourceInfo.Get(append(path, internal.EnumVal_nameTag))
1298		p.printElementString(nameSi, w, indent, evd.GetName())
1299		fmt.Fprint(w, "= ")
1300
1301		numSi := sourceInfo.Get(append(path, internal.EnumVal_numberTag))
1302		p.printElementString(numSi, w, indent, fmt.Sprintf("%d", evd.GetNumber()))
1303
1304		p.printOptionsShort(evd, evd.GetOptions(), mf, internal.EnumVal_optionsTag, w, sourceInfo, path, indent)
1305
1306		fmt.Fprint(w, ";")
1307	})
1308}
1309
1310func (p *Printer) printService(sd *desc.ServiceDescriptor, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) {
1311	si := sourceInfo.Get(path)
1312	p.printElement(true, si, w, indent, func(w *writer) {
1313		p.indent(w, indent)
1314
1315		fmt.Fprint(w, "service ")
1316		nameSi := sourceInfo.Get(append(path, internal.Service_nameTag))
1317		p.printElementString(nameSi, w, indent, sd.GetName())
1318		fmt.Fprintln(w, "{")
1319
1320		indent++
1321
1322		opts, err := p.extractOptions(sd, sd.GetOptions(), mf)
1323		if err != nil {
1324			if w.err == nil {
1325				w.err = err
1326			}
1327			return
1328		}
1329
1330		elements := elementAddrs{dsc: sd, opts: opts}
1331		elements.addrs = append(elements.addrs, optionsAsElementAddrs(internal.Service_optionsTag, -1, opts)...)
1332		for i := range sd.GetMethods() {
1333			elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Service_methodsTag, elementIndex: i})
1334		}
1335
1336		p.sort(elements, sourceInfo, path)
1337
1338		for i, el := range elements.addrs {
1339			if i > 0 {
1340				p.newLine(w)
1341			}
1342
1343			childPath := append(path, el.elementType, int32(el.elementIndex))
1344
1345			switch d := elements.at(el).(type) {
1346			case []option:
1347				p.printOptionsLong(d, w, sourceInfo, childPath, indent)
1348			case *desc.MethodDescriptor:
1349				p.printMethod(d, mf, w, sourceInfo, childPath, indent)
1350			}
1351		}
1352
1353		p.indent(w, indent-1)
1354		fmt.Fprintln(w, "}")
1355	})
1356}
1357
1358func (p *Printer) printMethod(mtd *desc.MethodDescriptor, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) {
1359	si := sourceInfo.Get(path)
1360	pkg := mtd.GetFile().GetPackage()
1361	p.printElement(true, si, w, indent, func(w *writer) {
1362		p.indent(w, indent)
1363
1364		fmt.Fprint(w, "rpc ")
1365		nameSi := sourceInfo.Get(append(path, internal.Method_nameTag))
1366		p.printElementString(nameSi, w, indent, mtd.GetName())
1367
1368		fmt.Fprint(w, "( ")
1369		inSi := sourceInfo.Get(append(path, internal.Method_inputTag))
1370		inName := p.qualifyName(pkg, pkg, mtd.GetInputType().GetFullyQualifiedName())
1371		if mtd.IsClientStreaming() {
1372			inName = "stream " + inName
1373		}
1374		p.printElementString(inSi, w, indent, inName)
1375
1376		fmt.Fprint(w, ") returns ( ")
1377
1378		outSi := sourceInfo.Get(append(path, internal.Method_outputTag))
1379		outName := p.qualifyName(pkg, pkg, mtd.GetOutputType().GetFullyQualifiedName())
1380		if mtd.IsServerStreaming() {
1381			outName = "stream " + outName
1382		}
1383		p.printElementString(outSi, w, indent, outName)
1384		fmt.Fprint(w, ") ")
1385
1386		opts, err := p.extractOptions(mtd, mtd.GetOptions(), mf)
1387		if err != nil {
1388			if w.err == nil {
1389				w.err = err
1390			}
1391			return
1392		}
1393
1394		if len(opts) > 0 {
1395			fmt.Fprintln(w, "{")
1396			indent++
1397
1398			elements := elementAddrs{dsc: mtd, opts: opts}
1399			elements.addrs = optionsAsElementAddrs(internal.Method_optionsTag, 0, opts)
1400			p.sort(elements, sourceInfo, path)
1401			path = append(path, internal.Method_optionsTag)
1402
1403			for i, addr := range elements.addrs {
1404				if i > 0 {
1405					p.newLine(w)
1406				}
1407				o := elements.at(addr).([]option)
1408				p.printOptionsLong(o, w, sourceInfo, path, indent)
1409			}
1410
1411			p.indent(w, indent-1)
1412			fmt.Fprintln(w, "}")
1413		} else {
1414			fmt.Fprint(w, ";")
1415		}
1416	})
1417}
1418
1419func (p *Printer) printOptionsLong(opts []option, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) {
1420	p.printOptions(opts, w, indent,
1421		func(i int32) *descriptor.SourceCodeInfo_Location {
1422			return sourceInfo.Get(append(path, i))
1423		},
1424		func(w *writer, indent int, opt option) {
1425			p.indent(w, indent)
1426			fmt.Fprint(w, "option ")
1427			p.printOption(opt.name, opt.val, w, indent)
1428			fmt.Fprint(w, ";")
1429		})
1430}
1431
1432func (p *Printer) printOptionsShort(dsc interface{}, optsMsg proto.Message, mf *dynamic.MessageFactory, optsTag int32, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) {
1433	d, ok := dsc.(desc.Descriptor)
1434	if !ok {
1435		d = dsc.(extensionRange).owner
1436	}
1437	opts, err := p.extractOptions(d, optsMsg, mf)
1438	if err != nil {
1439		if w.err == nil {
1440			w.err = err
1441		}
1442		return
1443	}
1444
1445	elements := elementAddrs{dsc: dsc, opts: opts}
1446	elements.addrs = optionsAsElementAddrs(optsTag, 0, opts)
1447	p.sort(elements, sourceInfo, path)
1448	p.printOptionElementsShort(elements, w, sourceInfo, path, indent)
1449}
1450
1451func (p *Printer) printOptionElementsShort(addrs elementAddrs, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) {
1452	if len(addrs.addrs) == 0 {
1453		return
1454	}
1455	first := true
1456	fmt.Fprint(w, "[")
1457	for _, addr := range addrs.addrs {
1458		opts := addrs.at(addr).([]option)
1459		var childPath []int32
1460		if addr.elementIndex < 0 {
1461			// pseudo-option
1462			childPath = append(path, int32(-addr.elementIndex))
1463		} else {
1464			childPath = append(path, addr.elementType, int32(addr.elementIndex))
1465		}
1466		p.printOptions(opts, w, inline(indent),
1467			func(i int32) *descriptor.SourceCodeInfo_Location {
1468				p := childPath
1469				if addr.elementIndex >= 0 {
1470					p = append(p, i)
1471				}
1472				return sourceInfo.Get(p)
1473			},
1474			func(w *writer, indent int, opt option) {
1475				if first {
1476					first = false
1477				} else {
1478					fmt.Fprint(w, ", ")
1479				}
1480				p.printOption(opt.name, opt.val, w, indent)
1481				fmt.Fprint(w, " ") // trailing space
1482			})
1483	}
1484	fmt.Fprint(w, "] ")
1485}
1486
1487func (p *Printer) printOptions(opts []option, w *writer, indent int, siFetch func(i int32) *descriptor.SourceCodeInfo_Location, fn func(w *writer, indent int, opt option)) {
1488	for i, opt := range opts {
1489		si := siFetch(int32(i))
1490		p.printElement(false, si, w, indent, func(w *writer) {
1491			fn(w, indent, opt)
1492		})
1493	}
1494}
1495
1496func inline(indent int) int {
1497	if indent < 0 {
1498		// already inlined
1499		return indent
1500	}
1501	// negative indent means inline; indent 2 stops further in case value wraps
1502	return -indent - 2
1503}
1504
1505func sortKeys(m map[interface{}]interface{}) []interface{} {
1506	res := make(sortedKeys, len(m))
1507	i := 0
1508	for k := range m {
1509		res[i] = k
1510		i++
1511	}
1512	sort.Sort(res)
1513	return ([]interface{})(res)
1514}
1515
1516type sortedKeys []interface{}
1517
1518func (k sortedKeys) Len() int {
1519	return len(k)
1520}
1521
1522func (k sortedKeys) Swap(i, j int) {
1523	k[i], k[j] = k[j], k[i]
1524}
1525
1526func (k sortedKeys) Less(i, j int) bool {
1527	switch i := k[i].(type) {
1528	case int32:
1529		return i < k[j].(int32)
1530	case uint32:
1531		return i < k[j].(uint32)
1532	case int64:
1533		return i < k[j].(int64)
1534	case uint64:
1535		return i < k[j].(uint64)
1536	case string:
1537		return i < k[j].(string)
1538	case bool:
1539		return !i && k[j].(bool)
1540	default:
1541		panic(fmt.Sprintf("invalid type for map key: %T", i))
1542	}
1543}
1544
1545func (p *Printer) printOption(name string, optVal interface{}, w *writer, indent int) {
1546	fmt.Fprintf(w, "%s = ", name)
1547
1548	switch optVal := optVal.(type) {
1549	case int32, uint32, int64, uint64:
1550		fmt.Fprintf(w, "%d", optVal)
1551	case float32, float64:
1552		fmt.Fprintf(w, "%f", optVal)
1553	case string:
1554		fmt.Fprintf(w, "%s", quotedString(optVal))
1555	case []byte:
1556		fmt.Fprintf(w, "%s", quotedString(string(optVal)))
1557	case bool:
1558		fmt.Fprintf(w, "%v", optVal)
1559	case ident:
1560		fmt.Fprintf(w, "%s", optVal)
1561	case *desc.EnumValueDescriptor:
1562		fmt.Fprintf(w, "%s", optVal.GetName())
1563	case proto.Message:
1564		// TODO: if value is too long, marshal to text format with indentation to
1565		// make output prettier (also requires correctly indenting subsequent lines)
1566
1567		// TODO: alternate approach so we can apply p.ForceFullyQualifiedNames
1568		// inside the resulting value?
1569
1570		fmt.Fprintf(w, "{ %s }", proto.CompactTextString(optVal))
1571	default:
1572		panic(fmt.Sprintf("unknown type of value %T for field %s", optVal, name))
1573	}
1574}
1575
1576type edgeKind int
1577
1578const (
1579	edgeKindOption edgeKind = iota
1580	edgeKindFile
1581	edgeKindMessage
1582	edgeKindField
1583	edgeKindOneOf
1584	edgeKindExtensionRange
1585	edgeKindEnum
1586	edgeKindEnumVal
1587	edgeKindService
1588	edgeKindMethod
1589)
1590
1591// edges in simple state machine for matching options paths
1592// whose prefix should be included in source info to handle
1593// the way options are printed (which cannot always include
1594// the full path from original source)
1595var edges = map[edgeKind]map[int32]edgeKind{
1596	edgeKindFile: {
1597		internal.File_optionsTag:    edgeKindOption,
1598		internal.File_messagesTag:   edgeKindMessage,
1599		internal.File_enumsTag:      edgeKindEnum,
1600		internal.File_extensionsTag: edgeKindField,
1601		internal.File_servicesTag:   edgeKindService,
1602	},
1603	edgeKindMessage: {
1604		internal.Message_optionsTag:        edgeKindOption,
1605		internal.Message_fieldsTag:         edgeKindField,
1606		internal.Message_oneOfsTag:         edgeKindOneOf,
1607		internal.Message_nestedMessagesTag: edgeKindMessage,
1608		internal.Message_enumsTag:          edgeKindEnum,
1609		internal.Message_extensionsTag:     edgeKindField,
1610		internal.Message_extensionRangeTag: edgeKindExtensionRange,
1611		// TODO: reserved range tag
1612	},
1613	edgeKindField: {
1614		internal.Field_optionsTag: edgeKindOption,
1615	},
1616	edgeKindOneOf: {
1617		internal.OneOf_optionsTag: edgeKindOption,
1618	},
1619	edgeKindExtensionRange: {
1620		internal.ExtensionRange_optionsTag: edgeKindOption,
1621	},
1622	edgeKindEnum: {
1623		internal.Enum_optionsTag: edgeKindOption,
1624		internal.Enum_valuesTag:  edgeKindEnumVal,
1625	},
1626	edgeKindEnumVal: {
1627		internal.EnumVal_optionsTag: edgeKindOption,
1628	},
1629	edgeKindService: {
1630		internal.Service_optionsTag: edgeKindOption,
1631		internal.Service_methodsTag: edgeKindMethod,
1632	},
1633	edgeKindMethod: {
1634		internal.Method_optionsTag: edgeKindOption,
1635	},
1636}
1637
1638func extendOptionLocations(sc internal.SourceInfoMap, locs []*descriptor.SourceCodeInfo_Location) {
1639	// we iterate in the order that locations appear in descriptor
1640	// for determinism (if we ranged over the map, order and thus
1641	// potentially results are non-deterministic)
1642	for _, loc := range locs {
1643		allowed := edges[edgeKindFile]
1644		for i := 0; i+1 < len(loc.Path); i += 2 {
1645			nextKind, ok := allowed[loc.Path[i]]
1646			if !ok {
1647				break
1648			}
1649			if nextKind == edgeKindOption {
1650				// We've found an option entry. This could be arbitrarily
1651				// deep (for options that nested messages) or it could end
1652				// abruptly (for non-repeated fields). But we need a path
1653				// that is exactly the path-so-far plus two: the option tag
1654				// and an optional index for repeated option fields (zero
1655				// for non-repeated option fields). This is used for
1656				// querying source info when printing options.
1657				// for sorting elements
1658				newPath := make([]int32, i+3)
1659				copy(newPath, loc.Path)
1660				sc.PutIfAbsent(newPath, loc)
1661				// we do another path of path-so-far plus two, but with
1662				// explicit zero index -- just in case this actual path has
1663				// an extra path element, but it's not an index (e.g the
1664				// option field is not repeated, but the source info we are
1665				// looking at indicates a tag of a nested field)
1666				newPath[len(newPath)-1] = 0
1667				sc.PutIfAbsent(newPath, loc)
1668				// finally, we need the path-so-far plus one, just the option
1669				// tag, for sorting option groups
1670				newPath = newPath[:len(newPath)-1]
1671				sc.PutIfAbsent(newPath, loc)
1672
1673				break
1674			} else {
1675				allowed = edges[nextKind]
1676			}
1677		}
1678	}
1679}
1680
1681func (p *Printer) extractOptions(dsc desc.Descriptor, opts proto.Message, mf *dynamic.MessageFactory) (map[int32][]option, error) {
1682	md, err := desc.LoadMessageDescriptorForMessage(opts)
1683	if err != nil {
1684		return nil, err
1685	}
1686	dm := mf.NewDynamicMessage(md)
1687	if err = dm.ConvertFrom(opts); err != nil {
1688		return nil, fmt.Errorf("failed convert %s to dynamic message: %v", md.GetFullyQualifiedName(), err)
1689	}
1690
1691	pkg := dsc.GetFile().GetPackage()
1692	var scope string
1693	if _, ok := dsc.(*desc.FileDescriptor); ok {
1694		scope = pkg
1695	} else {
1696		scope = dsc.GetFullyQualifiedName()
1697	}
1698
1699	options := map[int32][]option{}
1700	var uninterpreted []interface{}
1701	for _, fldset := range [][]*desc.FieldDescriptor{md.GetFields(), mf.GetExtensionRegistry().AllExtensionsForType(md.GetFullyQualifiedName())} {
1702		for _, fld := range fldset {
1703			if dm.HasField(fld) {
1704				val := dm.GetField(fld)
1705				var opts []option
1706				var name string
1707				if fld.IsExtension() {
1708					name = fmt.Sprintf("(%s)", p.qualifyName(pkg, scope, fld.GetFullyQualifiedName()))
1709				} else {
1710					name = fld.GetName()
1711				}
1712				switch val := val.(type) {
1713				case []interface{}:
1714					if fld.GetNumber() == internal.UninterpretedOptionsTag {
1715						// we handle uninterpreted options differently
1716						uninterpreted = val
1717						continue
1718					}
1719
1720					for _, e := range val {
1721						if fld.GetType() == descriptor.FieldDescriptorProto_TYPE_ENUM {
1722							ev := fld.GetEnumType().FindValueByNumber(e.(int32))
1723							if ev == nil {
1724								// have to skip unknown enum values :(
1725								continue
1726							}
1727							e = ev
1728						}
1729						var name string
1730						if fld.IsExtension() {
1731							name = fmt.Sprintf("(%s)", p.qualifyName(pkg, scope, fld.GetFullyQualifiedName()))
1732						} else {
1733							name = fld.GetName()
1734						}
1735						opts = append(opts, option{name: name, val: e})
1736					}
1737				case map[interface{}]interface{}:
1738					for k := range sortKeys(val) {
1739						v := val[k]
1740						vf := fld.GetMapValueType()
1741						if vf.GetType() == descriptor.FieldDescriptorProto_TYPE_ENUM {
1742							ev := vf.GetEnumType().FindValueByNumber(v.(int32))
1743							if ev == nil {
1744								// have to skip unknown enum values :(
1745								continue
1746							}
1747							v = ev
1748						}
1749						entry := mf.NewDynamicMessage(fld.GetMessageType())
1750						entry.SetFieldByNumber(1, k)
1751						entry.SetFieldByNumber(2, v)
1752						opts = append(opts, option{name: name, val: entry})
1753					}
1754				default:
1755					if fld.GetType() == descriptor.FieldDescriptorProto_TYPE_ENUM {
1756						ev := fld.GetEnumType().FindValueByNumber(val.(int32))
1757						if ev == nil {
1758							// have to skip unknown enum values :(
1759							continue
1760						}
1761						val = ev
1762					}
1763					opts = append(opts, option{name: name, val: val})
1764				}
1765				if len(opts) > 0 {
1766					options[fld.GetNumber()] = opts
1767				}
1768			}
1769		}
1770	}
1771
1772	// if there are uninterpreted options, add those too
1773	if len(uninterpreted) > 0 {
1774		opts := make([]option, len(uninterpreted))
1775		for i, u := range uninterpreted {
1776			var unint *descriptor.UninterpretedOption
1777			if un, ok := u.(*descriptor.UninterpretedOption); ok {
1778				unint = un
1779			} else {
1780				dm := u.(*dynamic.Message)
1781				unint = &descriptor.UninterpretedOption{}
1782				if err := dm.ConvertTo(unint); err != nil {
1783					return nil, err
1784				}
1785			}
1786
1787			var buf bytes.Buffer
1788			for ni, n := range unint.Name {
1789				if ni > 0 {
1790					buf.WriteByte('.')
1791				}
1792				if n.GetIsExtension() {
1793					fmt.Fprintf(&buf, "(%s)", n.GetNamePart())
1794				} else {
1795					buf.WriteString(n.GetNamePart())
1796				}
1797			}
1798
1799			var v interface{}
1800			switch {
1801			case unint.IdentifierValue != nil:
1802				v = ident(unint.GetIdentifierValue())
1803			case unint.StringValue != nil:
1804				v = string(unint.GetStringValue())
1805			case unint.DoubleValue != nil:
1806				v = unint.GetDoubleValue()
1807			case unint.PositiveIntValue != nil:
1808				v = unint.GetPositiveIntValue()
1809			case unint.NegativeIntValue != nil:
1810				v = unint.GetNegativeIntValue()
1811			case unint.AggregateValue != nil:
1812				v = ident(unint.GetAggregateValue())
1813			}
1814
1815			opts[i] = option{name: buf.String(), val: v}
1816		}
1817		options[internal.UninterpretedOptionsTag] = opts
1818	}
1819
1820	return options, nil
1821}
1822
1823func optionsAsElementAddrs(optionsTag int32, order int, opts map[int32][]option) []elementAddr {
1824	var optAddrs []elementAddr
1825	for tag := range opts {
1826		optAddrs = append(optAddrs, elementAddr{elementType: optionsTag, elementIndex: int(tag), order: order})
1827	}
1828	sort.Sort(optionsByName{addrs: optAddrs, opts: opts})
1829	return optAddrs
1830}
1831
1832// quotedString implements the text format for string literals for protocol
1833// buffers. This form is also acceptable for string literals in option values
1834// by the protocol buffer compiler, protoc.
1835func quotedString(s string) string {
1836	var b bytes.Buffer
1837	// use WriteByte here to get any needed indent
1838	b.WriteByte('"')
1839	// Loop over the bytes, not the runes.
1840	for i := 0; i < len(s); i++ {
1841		// Divergence from C++: we don't escape apostrophes.
1842		// There's no need to escape them, and the C++ parser
1843		// copes with a naked apostrophe.
1844		switch c := s[i]; c {
1845		case '\n':
1846			b.WriteString("\\n")
1847		case '\r':
1848			b.WriteString("\\r")
1849		case '\t':
1850			b.WriteString("\\t")
1851		case '"':
1852			b.WriteString("\\")
1853		case '\\':
1854			b.WriteString("\\\\")
1855		default:
1856			if c >= 0x20 && c < 0x7f {
1857				b.WriteByte(c)
1858			} else {
1859				fmt.Fprintf(&b, "\\%03o", c)
1860			}
1861		}
1862	}
1863	b.WriteByte('"')
1864
1865	return b.String()
1866}
1867
1868type elementAddr struct {
1869	elementType  int32
1870	elementIndex int
1871	order        int
1872}
1873
1874type elementAddrs struct {
1875	addrs []elementAddr
1876	dsc   interface{}
1877	opts  map[int32][]option
1878}
1879
1880func (a elementAddrs) Len() int {
1881	return len(a.addrs)
1882}
1883
1884func (a elementAddrs) Less(i, j int) bool {
1885	// explicit order is considered first
1886	if a.addrs[i].order < a.addrs[j].order {
1887		return true
1888	} else if a.addrs[i].order > a.addrs[j].order {
1889		return false
1890	}
1891	// if order is equal, sort by element type
1892	if a.addrs[i].elementType < a.addrs[j].elementType {
1893		return true
1894	} else if a.addrs[i].elementType > a.addrs[j].elementType {
1895		return false
1896	}
1897
1898	di := a.at(a.addrs[i])
1899	dj := a.at(a.addrs[j])
1900
1901	switch vi := di.(type) {
1902	case *desc.FieldDescriptor:
1903		// fields are ordered by tag number
1904		vj := dj.(*desc.FieldDescriptor)
1905		// regular fields before extensions; extensions grouped by extendee
1906		if !vi.IsExtension() && vj.IsExtension() {
1907			return true
1908		} else if vi.IsExtension() && !vj.IsExtension() {
1909			return false
1910		} else if vi.IsExtension() && vj.IsExtension() {
1911			if vi.GetOwner() != vj.GetOwner() {
1912				return vi.GetOwner().GetFullyQualifiedName() < vj.GetOwner().GetFullyQualifiedName()
1913			}
1914		}
1915		return vi.GetNumber() < vj.GetNumber()
1916
1917	case *desc.EnumValueDescriptor:
1918		// enum values ordered by number then name
1919		vj := dj.(*desc.EnumValueDescriptor)
1920		if vi.GetNumber() == vj.GetNumber() {
1921			return vi.GetName() < vj.GetName()
1922		}
1923		return vi.GetNumber() < vj.GetNumber()
1924
1925	case *descriptor.DescriptorProto_ExtensionRange:
1926		// extension ranges ordered by tag
1927		return vi.GetStart() < dj.(*descriptor.DescriptorProto_ExtensionRange).GetStart()
1928
1929	case reservedRange:
1930		// reserved ranges ordered by tag, too
1931		return vi.start < dj.(reservedRange).start
1932
1933	case string:
1934		// reserved names lexically sorted
1935		return vi < dj.(string)
1936
1937	case pkg:
1938		// reserved names lexically sorted
1939		return vi < dj.(pkg)
1940
1941	case imp:
1942		// reserved names lexically sorted
1943		return vi < dj.(imp)
1944
1945	case []option:
1946		// options sorted by name, extensions last
1947		return optionLess(vi, dj.([]option))
1948
1949	default:
1950		// all other descriptors ordered by name
1951		return di.(desc.Descriptor).GetName() < dj.(desc.Descriptor).GetName()
1952	}
1953}
1954
1955func (a elementAddrs) Swap(i, j int) {
1956	a.addrs[i], a.addrs[j] = a.addrs[j], a.addrs[i]
1957}
1958
1959func (a elementAddrs) at(addr elementAddr) interface{} {
1960	switch dsc := a.dsc.(type) {
1961	case *desc.FileDescriptor:
1962		switch addr.elementType {
1963		case internal.File_packageTag:
1964			return pkg(dsc.GetPackage())
1965		case internal.File_dependencyTag:
1966			return imp(dsc.AsFileDescriptorProto().GetDependency()[addr.elementIndex])
1967		case internal.File_optionsTag:
1968			return a.opts[int32(addr.elementIndex)]
1969		case internal.File_messagesTag:
1970			return dsc.GetMessageTypes()[addr.elementIndex]
1971		case internal.File_enumsTag:
1972			return dsc.GetEnumTypes()[addr.elementIndex]
1973		case internal.File_servicesTag:
1974			return dsc.GetServices()[addr.elementIndex]
1975		case internal.File_extensionsTag:
1976			return dsc.GetExtensions()[addr.elementIndex]
1977		}
1978	case *desc.MessageDescriptor:
1979		switch addr.elementType {
1980		case internal.Message_optionsTag:
1981			return a.opts[int32(addr.elementIndex)]
1982		case internal.Message_fieldsTag:
1983			return dsc.GetFields()[addr.elementIndex]
1984		case internal.Message_nestedMessagesTag:
1985			return dsc.GetNestedMessageTypes()[addr.elementIndex]
1986		case internal.Message_enumsTag:
1987			return dsc.GetNestedEnumTypes()[addr.elementIndex]
1988		case internal.Message_extensionsTag:
1989			return dsc.GetNestedExtensions()[addr.elementIndex]
1990		case internal.Message_extensionRangeTag:
1991			return dsc.AsDescriptorProto().GetExtensionRange()[addr.elementIndex]
1992		case internal.Message_reservedRangeTag:
1993			rng := dsc.AsDescriptorProto().GetReservedRange()[addr.elementIndex]
1994			return reservedRange{start: rng.GetStart(), end: rng.GetEnd() - 1}
1995		case internal.Message_reservedNameTag:
1996			return dsc.AsDescriptorProto().GetReservedName()[addr.elementIndex]
1997		}
1998	case *desc.FieldDescriptor:
1999		if addr.elementType == internal.Field_optionsTag {
2000			return a.opts[int32(addr.elementIndex)]
2001		}
2002	case *desc.OneOfDescriptor:
2003		switch addr.elementType {
2004		case internal.OneOf_optionsTag:
2005			return a.opts[int32(addr.elementIndex)]
2006		case -internal.Message_fieldsTag:
2007			return dsc.GetOwner().GetFields()[addr.elementIndex]
2008		}
2009	case *desc.EnumDescriptor:
2010		switch addr.elementType {
2011		case internal.Enum_optionsTag:
2012			return a.opts[int32(addr.elementIndex)]
2013		case internal.Enum_valuesTag:
2014			return dsc.GetValues()[addr.elementIndex]
2015		case internal.Enum_reservedRangeTag:
2016			rng := dsc.AsEnumDescriptorProto().GetReservedRange()[addr.elementIndex]
2017			return reservedRange{start: rng.GetStart(), end: rng.GetEnd()}
2018		case internal.Enum_reservedNameTag:
2019			return dsc.AsEnumDescriptorProto().GetReservedName()[addr.elementIndex]
2020		}
2021	case *desc.EnumValueDescriptor:
2022		if addr.elementType == internal.EnumVal_optionsTag {
2023			return a.opts[int32(addr.elementIndex)]
2024		}
2025	case *desc.ServiceDescriptor:
2026		switch addr.elementType {
2027		case internal.Service_optionsTag:
2028			return a.opts[int32(addr.elementIndex)]
2029		case internal.Service_methodsTag:
2030			return dsc.GetMethods()[addr.elementIndex]
2031		}
2032	case *desc.MethodDescriptor:
2033		if addr.elementType == internal.Method_optionsTag {
2034			return a.opts[int32(addr.elementIndex)]
2035		}
2036	case extensionRange:
2037		if addr.elementType == internal.ExtensionRange_optionsTag {
2038			return a.opts[int32(addr.elementIndex)]
2039		}
2040	}
2041
2042	panic(fmt.Sprintf("location for unknown field %d of %T", addr.elementType, a.dsc))
2043}
2044
2045type extensionRange struct {
2046	owner    *desc.MessageDescriptor
2047	extRange *descriptor.DescriptorProto_ExtensionRange
2048}
2049
2050type elementSrcOrder struct {
2051	elementAddrs
2052	sourceInfo internal.SourceInfoMap
2053	prefix     []int32
2054}
2055
2056func (a elementSrcOrder) Less(i, j int) bool {
2057	ti := a.addrs[i].elementType
2058	ei := a.addrs[i].elementIndex
2059
2060	tj := a.addrs[j].elementType
2061	ej := a.addrs[j].elementIndex
2062
2063	var si, sj *descriptor.SourceCodeInfo_Location
2064	if ei < 0 {
2065		si = a.sourceInfo.Get(append(a.prefix, -int32(ei)))
2066	} else if ti < 0 {
2067		p := make([]int32, len(a.prefix)-2)
2068		copy(p, a.prefix)
2069		si = a.sourceInfo.Get(append(p, ti, int32(ei)))
2070	} else {
2071		si = a.sourceInfo.Get(append(a.prefix, ti, int32(ei)))
2072	}
2073	if ej < 0 {
2074		sj = a.sourceInfo.Get(append(a.prefix, -int32(ej)))
2075	} else if tj < 0 {
2076		p := make([]int32, len(a.prefix)-2)
2077		copy(p, a.prefix)
2078		sj = a.sourceInfo.Get(append(p, tj, int32(ej)))
2079	} else {
2080		sj = a.sourceInfo.Get(append(a.prefix, tj, int32(ej)))
2081	}
2082
2083	if (si == nil) != (sj == nil) {
2084		// generally, we put unknown elements after known ones;
2085		// except package, imports, and option elements go first
2086
2087		// i will be unknown and j will be known
2088		swapped := false
2089		if si != nil {
2090			ti, tj = tj, ti
2091			swapped = true
2092		}
2093		switch a.dsc.(type) {
2094		case *desc.FileDescriptor:
2095			// NB: These comparisons are *trying* to get things ordered so that
2096			// 1) If the package element has no source info, it appears _first_.
2097			// 2) If any import element has no source info, it appears _after_
2098			//    the package element but _before_ any other element.
2099			// 3) If any option element has no source info, it appears _after_
2100			//    the package and import elements but _before_ any other element.
2101			// If the package, imports, and options are all missing source info,
2102			// this will sort them all to the top in expected order. But if they
2103			// are mixed (some _do_ have source info, some do not), and elements
2104			// with source info have spans that positions them _after_ other
2105			// elements in the file, then this Less function will be unstable
2106			// since the above dual objectives for imports and options ("before
2107			// this but after that") may be in conflict with one another. This
2108			// should not cause any problems, other than elements being possibly
2109			// sorted in a confusing order.
2110			//
2111			// Well-formed descriptors should instead have consistent source
2112			// info: either all elements have source info or none do. So this
2113			// should not be an issue in practice.
2114			if ti == internal.File_packageTag {
2115				return !swapped
2116			}
2117			if ti == internal.File_dependencyTag {
2118				if tj == internal.File_packageTag {
2119					// imports will come *after* package
2120					return swapped
2121				}
2122				return !swapped
2123			}
2124			if ti == internal.File_optionsTag {
2125				if tj == internal.File_packageTag || tj == internal.File_dependencyTag {
2126					// options will come *after* package and imports
2127					return swapped
2128				}
2129				return !swapped
2130			}
2131		case *desc.MessageDescriptor:
2132			if ti == internal.Message_optionsTag {
2133				return !swapped
2134			}
2135		case *desc.EnumDescriptor:
2136			if ti == internal.Enum_optionsTag {
2137				return !swapped
2138			}
2139		case *desc.ServiceDescriptor:
2140			if ti == internal.Service_optionsTag {
2141				return !swapped
2142			}
2143		}
2144		return swapped
2145
2146	} else if si == nil || sj == nil {
2147		// let stable sort keep unknown elements in same relative order
2148		return false
2149	}
2150
2151	for idx := 0; idx < len(sj.Span); idx++ {
2152		if idx >= len(si.Span) {
2153			return true
2154		}
2155		if si.Span[idx] < sj.Span[idx] {
2156			return true
2157		}
2158		if si.Span[idx] > sj.Span[idx] {
2159			return false
2160		}
2161	}
2162	return false
2163}
2164
2165type optionsByName struct {
2166	addrs []elementAddr
2167	opts  map[int32][]option
2168}
2169
2170func (o optionsByName) Len() int {
2171	return len(o.addrs)
2172}
2173
2174func (o optionsByName) Less(i, j int) bool {
2175	oi := o.opts[int32(o.addrs[i].elementIndex)]
2176	oj := o.opts[int32(o.addrs[j].elementIndex)]
2177	return optionLess(oi, oj)
2178}
2179
2180func optionLess(i, j []option) bool {
2181	ni := i[0].name
2182	nj := j[0].name
2183	if ni[0] != '(' && nj[0] == '(' {
2184		return true
2185	} else if ni[0] == '(' && nj[0] != '(' {
2186		return false
2187	}
2188	return ni < nj
2189}
2190
2191func (o optionsByName) Swap(i, j int) {
2192	o.addrs[i], o.addrs[j] = o.addrs[j], o.addrs[i]
2193}
2194
2195func (p *Printer) printElement(isDecriptor bool, si *descriptor.SourceCodeInfo_Location, w *writer, indent int, el func(*writer)) {
2196	includeComments := isDecriptor || p.includeCommentType(CommentsTokens)
2197
2198	if includeComments && si != nil {
2199		p.printLeadingComments(si, w, indent)
2200	}
2201	el(w)
2202	if includeComments && si != nil {
2203		p.printTrailingComments(si, w, indent)
2204	}
2205	if indent >= 0 && !w.newline {
2206		// if we're not printing inline but element did not have trailing newline, add one now
2207		fmt.Fprintln(w)
2208	}
2209}
2210
2211func (p *Printer) printElementString(si *descriptor.SourceCodeInfo_Location, w *writer, indent int, str string) {
2212	p.printElement(false, si, w, inline(indent), func(w *writer) {
2213		fmt.Fprintf(w, "%s ", str)
2214	})
2215}
2216
2217func (p *Printer) includeCommentType(c CommentType) bool {
2218	return (p.OmitComments & c) == 0
2219}
2220
2221func (p *Printer) printLeadingComments(si *descriptor.SourceCodeInfo_Location, w *writer, indent int) bool {
2222	endsInNewLine := false
2223
2224	if p.includeCommentType(CommentsDetached) {
2225		for _, c := range si.GetLeadingDetachedComments() {
2226			if p.printComment(c, w, indent, true) {
2227				// if comment ended in newline, add another newline to separate
2228				// this comment from the next
2229				p.newLine(w)
2230				endsInNewLine = true
2231			} else if indent < 0 {
2232				// comment did not end in newline and we are trying to inline?
2233				// just add a space to separate this comment from what follows
2234				fmt.Fprint(w, " ")
2235				endsInNewLine = false
2236			} else {
2237				// comment did not end in newline and we are *not* trying to inline?
2238				// add newline to end of comment and add another to separate this
2239				// comment from what follows
2240				fmt.Fprintln(w) // needed to end comment, regardless of p.Compact
2241				p.newLine(w)
2242				endsInNewLine = true
2243			}
2244		}
2245	}
2246
2247	if p.includeCommentType(CommentsLeading) && si.GetLeadingComments() != "" {
2248		endsInNewLine = p.printComment(si.GetLeadingComments(), w, indent, true)
2249		if !endsInNewLine {
2250			if indent >= 0 {
2251				// leading comment didn't end with newline but needs one
2252				// (because we're *not* inlining)
2253				fmt.Fprintln(w) // needed to end comment, regardless of p.Compact
2254				endsInNewLine = true
2255			} else {
2256				// space between comment and following element when inlined
2257				fmt.Fprint(w, " ")
2258			}
2259		}
2260	}
2261
2262	return endsInNewLine
2263}
2264
2265func (p *Printer) printTrailingComments(si *descriptor.SourceCodeInfo_Location, w *writer, indent int) {
2266	if p.includeCommentType(CommentsTrailing) && si.GetTrailingComments() != "" {
2267		if !p.printComment(si.GetTrailingComments(), w, indent, p.TrailingCommentsOnSeparateLine) && indent >= 0 {
2268			// trailing comment didn't end with newline but needs one
2269			// (because we're *not* inlining)
2270			fmt.Fprintln(w) // needed to end comment, regardless of p.Compact
2271		} else if indent < 0 {
2272			fmt.Fprint(w, " ")
2273		}
2274	}
2275}
2276
2277func (p *Printer) printComment(comments string, w *writer, indent int, forceNextLine bool) bool {
2278	if comments == "" {
2279		return false
2280	}
2281
2282	var multiLine bool
2283	if indent < 0 {
2284		// use multi-line style when inlining
2285		multiLine = true
2286	} else {
2287		multiLine = p.PreferMultiLineStyleComments
2288	}
2289	if multiLine && strings.Contains(comments, "*/") {
2290		// can't emit '*/' in a multi-line style comment
2291		multiLine = false
2292	}
2293
2294	lines := strings.Split(comments, "\n")
2295
2296	// first, remove leading and trailing blank lines
2297	if lines[0] == "" {
2298		lines = lines[1:]
2299	}
2300	if lines[len(lines)-1] == "" {
2301		lines = lines[:len(lines)-1]
2302	}
2303	if len(lines) == 0 {
2304		return false
2305	}
2306
2307	if indent >= 0 && !w.newline {
2308		// last element did not have trailing newline, so we
2309		// either need to tack on newline or, if comment is
2310		// just one line, inline it on the end
2311		if forceNextLine || len(lines) > 1 {
2312			fmt.Fprintln(w)
2313		} else {
2314			if !w.space {
2315				fmt.Fprint(w, " ")
2316			}
2317			indent = inline(indent)
2318		}
2319	}
2320
2321	if len(lines) == 1 && multiLine {
2322		p.indent(w, indent)
2323		line := lines[0]
2324		if line[0] == ' ' && line[len(line)-1] != ' ' {
2325			// add trailing space for symmetry
2326			line += " "
2327		}
2328		fmt.Fprintf(w, "/*%s*/", line)
2329		if indent >= 0 {
2330			fmt.Fprintln(w)
2331			return true
2332		}
2333		return false
2334	}
2335
2336	if multiLine {
2337		// multi-line style comments that actually span multiple lines
2338		// get a blank line before and after so that comment renders nicely
2339		lines = append(lines, "", "")
2340		copy(lines[1:], lines)
2341		lines[0] = ""
2342	}
2343
2344	for i, l := range lines {
2345		p.maybeIndent(w, indent, i > 0)
2346		if multiLine {
2347			if i == 0 {
2348				// first line
2349				fmt.Fprintf(w, "/*%s\n", strings.TrimRight(l, " \t"))
2350			} else if i == len(lines)-1 {
2351				// last line
2352				if l == "" {
2353					fmt.Fprint(w, " */")
2354				} else {
2355					fmt.Fprintf(w, " *%s*/", l)
2356				}
2357				if indent >= 0 {
2358					fmt.Fprintln(w)
2359				}
2360			} else {
2361				fmt.Fprintf(w, " *%s\n", strings.TrimRight(l, " \t"))
2362			}
2363		} else {
2364			fmt.Fprintf(w, "//%s\n", strings.TrimRight(l, " \t"))
2365		}
2366	}
2367
2368	// single-line comments always end in newline; multi-line comments only
2369	// end in newline for non-negative (e.g. non-inlined) indentation
2370	return !multiLine || indent >= 0
2371}
2372
2373func (p *Printer) indent(w io.Writer, indent int) {
2374	for i := 0; i < indent; i++ {
2375		fmt.Fprint(w, p.Indent)
2376	}
2377}
2378
2379func (p *Printer) maybeIndent(w io.Writer, indent int, requireIndent bool) {
2380	if indent < 0 && requireIndent {
2381		p.indent(w, -indent)
2382	} else {
2383		p.indent(w, indent)
2384	}
2385}
2386
2387type writer struct {
2388	io.Writer
2389	err     error
2390	space   bool
2391	newline bool
2392}
2393
2394func newWriter(w io.Writer) *writer {
2395	return &writer{Writer: w, newline: true}
2396}
2397
2398func (w *writer) Write(p []byte) (int, error) {
2399	if len(p) == 0 {
2400		return 0, nil
2401	}
2402
2403	w.newline = false
2404
2405	if w.space {
2406		// skip any trailing space if the following
2407		// character is semicolon, comma, or close bracket
2408		if p[0] != ';' && p[0] != ',' && p[0] != ']' {
2409			_, err := w.Writer.Write([]byte{' '})
2410			if err != nil {
2411				w.err = err
2412				return 0, err
2413			}
2414		}
2415		w.space = false
2416	}
2417
2418	if p[len(p)-1] == ' ' {
2419		w.space = true
2420		p = p[:len(p)-1]
2421	}
2422	if len(p) > 0 && p[len(p)-1] == '\n' {
2423		w.newline = true
2424	}
2425
2426	num, err := w.Writer.Write(p)
2427	if err != nil {
2428		w.err = err
2429	} else if w.space {
2430		// pretend space was written
2431		num++
2432	}
2433	return num, err
2434}
2435