How can I re generate source code after compiled
iyaozhen opened this issue · 5 comments
I use protocompile.Compiler
xxx.proto and delete it.
And I use protoreflect.MessageDescriptor
generate json template.
for example:
message Result {
required string url = 1;
optional string title = 2;
repeated string snippets = 3;
}
got json template
{
"url": "",
"title": "",
"snippets": []
}
But user fill json depend on source code, like some comment、enum.
I try sourceinfo.GenerateSourceInfo
, just got some infomation, not original text.
And thank you for provided this lib.
@iyaozhen, I'm not quite sure I understand the scenario. But maybe what you are looking for is to set the compiler's SourceInfoMode
field to SourceInfoStandard
(it defaults to SourceInfoNone
).
I used SourceInfoMode
, But How can i get original source code.
func Test_getMessageSource2(t *testing.T) {
sourceCode := `
syntax = "proto2";
package pb3;
// some comment
message Result {
required string url = 1;
optional string title = 2;
repeated string snippets = 3;
}
`
ctx := context.Background()
resolver := &protocompile.SourceResolver{
Accessor: protocompile.SourceAccessorFromMap(map[string]string{"pb3.proto": sourceCode}),
}
compiler := protocompile.Compiler{
Resolver: resolver,
Reporter: nil,
SourceInfoMode: protocompile.SourceInfoExtraComments,
RetainASTs: true,
}
linkFiles, err := compiler.Compile(ctx, "pb3.proto")
if err != nil {
t.Error(err)
return
}
linkFile := linkFiles.FindFileByPath("pb3.proto")
//messageDesc := linkFile.FindDescriptorByName("pb3.Result")
linkerResult := linkFile.(linker.Result)
sourceInfo := sourceinfo.GenerateSourceInfoWithExtraComments(linkerResult.AST(), nil)
// What can I do ?
}
I want convert(rebuild) linkerResult
to sourceCode
.
Forgive my poor English.
Okay. This library wasn't really designed to go that direction, but there are a couple of possibilities:
- Set the compiler's
RetainASTs
field to true. This will keep the AST for the source code in thelinker.Result
. From the AST you can reconstruct the original source as seen below. - Instead of keeping the original source code, you could also generate the source code from the descriptor. You can do this using a package in a different library: github.com/jhump/protoreflect/desc/protoprint. When generating source from the descriptor, it is possible that some comments may not be preserved (because not all comments in the file are captured in the descriptor's source info). And the formatting will, of course, differ from the original source. But this technique can be used even if you don't have an AST from a parsed file.
Example of recovering original source from AST:
result := fileDescriptor.(linker.Result)
file := result.AST()
items := file.Items()
current := items.First()
// loop through all comments and tokens in AST
for {
itemInfo := file.ItemInfo(current)
// print each item
_, err := fmt.Print(itemInfo.LeadingWhitespace())
if err != nil {
panic(err)
}
_, err = fmt.Print(itemInfo.RawText())
if err != nil {
panic(err)
}
var ok bool
current, ok = items.Next(current)
if !ok {
break // done
}
}
Example of generating source code from descriptor:
fd, err := desc.WrapFile(fileDescriptor)
if err != nil {
panic(err)
}
var printer protoprint.Printer
err = printer.PrintProtoFile(fd, os.Stdout)
if err != nil {
panic(err)
}
Thanks very mush, let me try it.
I find a other way to resolve this problem. Need store idl file orginal content.
func findMessageSourceCodeByDesc(linkFiles linker.Files, filesContent map[string][]byte,
messageDesc protoreflect.Descriptor) string {
filePath := messageDesc.ParentFile().Path()
linkFile := linkFiles.FindFileByPath(filePath)
if linkFile == nil {
return ""
}
sourceLocations := linkFile.SourceLocations()
fileContent, ok := filesContent[filePath]
if !ok {
return ""
}
fileContentLines := strings.Split(string(fileContent), "\n")
messageContentLines := make([]string, 0)
sourceLocation := sourceLocations.ByDescriptor(messageDesc)
leadingComments := sourceLocation.LeadingComments
if len(leadingComments) > 0 {
for _, comment := range strings.Split(leadingComments, "\n") {
comment = strings.TrimSpace(comment)
if len(comment) == 0 {
continue
}
messageContentLines = append(messageContentLines, fmt.Sprintf("// %s", comment))
}
}
messageContentLines = append(messageContentLines, fileContentLines[sourceLocation.StartLine:sourceLocation.
EndLine+1]...)
return strings.Join(messageContentLines, "\n")
}