Repository: tableauio/tableau
Pull request #222 (2025-04-15 11:08:35)

protogen(xml): support custom metasheet name


Patch:
diff --git a/internal/importer/book/sheet.go b/internal/importer/book/sheet.go
index 2b1e319..83b413a 100644
--- a/internal/importer/book/sheet.go
+++ b/internal/importer/book/sheet.go
@@ -2,6 +2,7 @@ package book
 
 import (
 	"bytes"
+	"fmt"
 	"strings"
 
 	"github.com/tableauio/tableau/proto/tableaupb"
@@ -25,9 +26,11 @@ const BookNameInMetasheet = "#"
 // NOTE: If will not change MetasheetName value if the specified name
 // is empty.
 func SetMetasheetName(name string) {
-	if name != "" {
-		MetasheetName = name
+	if !strings.HasPrefix(name, "@") {
+		panic(fmt.Sprintf("metasheet name must start with '@': %q", name))
 	}
+	// set cumtomized metasheet name
+	MetasheetName = name
 }
 
 type SheetParser interface {
diff --git a/internal/importer/xml.go b/internal/importer/xml.go
index 51c2573..35d6541 100644
--- a/internal/importer/xml.go
+++ b/internal/importer/xml.go
@@ -6,7 +6,6 @@ import (
 	"encoding/xml"
 	"fmt"
 	"io"
-	"log"
 	"os"
 	"path/filepath"
 	"regexp"
@@ -15,6 +14,7 @@ import (
 	"github.com/subchen/go-xmldom"
 	"github.com/tableauio/tableau/internal/importer/book"
 	"github.com/tableauio/tableau/internal/types"
+	"github.com/tableauio/tableau/log"
 	"github.com/tableauio/tableau/xerrors"
 )
 
@@ -23,34 +23,20 @@ type XMLImporter struct {
 }
 
 var (
-	attrRegexp      *regexp.Regexp
-	tagRegexp       *regexp.Regexp
-	metasheetRegexp *regexp.Regexp
+	attrRegexp *regexp.Regexp
+	tagRegexp  *regexp.Regexp
 )
 
 const (
-	xmlProlog             = `<?xml version='1.0' encoding='UTF-8'?>`
-	atTableauDisplacement = `ATABLEAU`
-	atTypeDisplacement    = "ATYPE"
-	ungreedyPropGroup     = `(\|\{[^\{\}]+\})?`                       // e.g.: |{default:"100"}
-	metasheetItemBlock    = `<Item(\s+\S+\s*=\s*("\S+"|'\S+'))+\s*/>` // e.g.: <Item Sheet="XXXConf" Sep="|"/>
-	sheetBlock            = `<%v(>(.*\n)*</%v>|\s*/>)`                // e.g.: <XXXConf>...</XXXConf>
+	xmlProlog          = `<?xml version='1.0' encoding='UTF-8'?>`
+	atTypeDisplacement = "ATYPE"
+	ungreedyPropGroup  = `(\|\{[^\{\}]+\})?` // e.g.: |{default:"100"}
+	metasheetItemBlock = `<Item\s+[^>]*\/>`  // e.g.: <Item Sheet="XXXConf" Sep="|"/>
 )
 
 func init() {
 	attrRegexp = regexp.MustCompile(`\s*=\s*("|')` + types.TypeGroup + ungreedyPropGroup + `("|')`) // e.g.: = "int32|{range:"1,~"}"
 	tagRegexp = regexp.MustCompile(`>` + types.TypeGroup + ungreedyPropGroup + `</`)                // e.g.: >int32|{range:"1,~"}</
-	// metasheet regexp, e.g.:
-	// <!--
-	// <@TABLEAU>
-	// 		<Item Sheet="Server" />
-	// </@TABLEAU>
-
-	// <Server>
-	// 		<Weight Num="map<uint32, Weight>"/>
-	// </Server>
-	// -->
-	metasheetRegexp = regexp.MustCompile(fmt.Sprintf(`<!--\s+(<%v(>(\s+`+metasheetItemBlock+`\s+)*</%v>|\s*/>)(.*\n)+?)-->\s*\n`, book.MetasheetName, book.MetasheetName))
 }
 
 func NewXMLImporter(filename string, sheets []string, parser book.SheetParser, mode ImporterMode, cloned bool) (*XMLImporter, error) {
@@ -59,7 +45,7 @@ func NewXMLImporter(filename string, sheets []string, parser book.SheetParser, m
 	if mode == Protogen {
 		book, err = readXMLBookWithOnlySchemaSheet(filename, parser)
 		if err != nil {
-			return nil, xerrors.Wrapf(err, "failed to read csv book: %s", filename)
+			return nil, xerrors.Wrapf(err, "failed to read xml book: %s", filename)
 		}
 		if err := book.ParseMetaAndPurge(); err != nil {
 			return nil, xerrors.Wrapf(err, "failed to parse metasheet")
@@ -67,7 +53,7 @@ func NewXMLImporter(filename string, sheets []string, parser book.SheetParser, m
 	} else {
 		book, err = readXMLBook(filename, parser)
 		if err != nil {
-			return nil, xerrors.Wrapf(err, "failed to read csv book: %s", filename)
+			return nil, xerrors.Wrapf(err, "failed to read xml book: %s", filename)
 		}
 	}
 
@@ -85,7 +71,23 @@ func readXMLBook(filename string, parser book.SheetParser) (*book.Book, error) {
 	if err != nil {
 		return nil, err
 	}
-	rawDocs, err := extractRawXMLDocuments(string(content))
+	metasheet := splitXMLMetasheet(string(content))
+	rawDocs, err := extractRawXMLDocuments(metasheet)
+	if err != nil {
+		return nil, err
+	}
+	for _, rawDoc := range rawDocs {
+		doc, err := xmldom.ParseXML(rawDoc)
+		if err != nil {
+			return nil, err
+		}
+		sheet, err := parseXMLSheet(doc, Protogen)
+		if err != nil {
+			return nil, xerrors.Wrapf(err, "file: %s", filename)
+		}
+		newBook.AddSheet(sheet)
+	}
+	rawDocs, err = extractRawXMLDocuments(string(content))
 	if err != nil {
 		return nil, err
 	}
@@ -130,10 +132,14 @@ func readXMLBookWithOnlySchemaSheet(filename string, parser book.SheetParser) (*
 	return newBook, nil
 }
 
+func xmlMetasheetName() string {
+	return strings.Replace(book.MetasheetName, "@", "AT", 1)
+}
+
 func parseXMLSheet(doc *xmldom.Document, mode ImporterMode) (*book.Sheet, error) {
 	name := doc.Root.Name
 	if mode == Protogen {
-		if name == atTableauDisplacement {
+		if name == xmlMetasheetName() {
 			return parseXMLMetaSheet(doc)
 		}
 		name = "@" + name
@@ -579,6 +585,17 @@ func parseXMLAttribute(bnode *book.Node, attrName, attrValue string, isFirstAttr
 
 // splitXMLMetasheet splits metasheet from xml notes
 func splitXMLMetasheet(content string) string {
+	// metasheet regexp, e.g.:
+	// <!--
+	// <@TABLEAU>
+	// 		<Item Sheet="Server" />
+	// </@TABLEAU>
+
+	// <Server>
+	// 		<Weight Num="map<uint32, Weight>"/>
+	// </Server>
+	// -->
+	metasheetRegexp := regexp.MustCompile(fmt.Sprintf(`<!--([\s\S]*?<%v(?:>(?:\s+`+metasheetItemBlock+`\s+)*</%v>|\s*/>)[\s\S]*?)-->`, book.MetasheetName, book.MetasheetName))
 	matches := metasheetRegexp.FindStringSubmatch(content)
 	if len(matches) < 2 {
 		return ""
@@ -593,7 +610,8 @@ func splitXMLMetasheet(content string) string {
 		return ""
 	}
 	metasheet := matches[1]
-	metasheet = strings.ReplaceAll(metasheet, book.MetasheetName, atTableauDisplacement)
+	metasheet = strings.ReplaceAll(metasheet, "<@", "<AT")
+	metasheet = strings.ReplaceAll(metasheet, "</@", "</AT")
 	metasheet = strings.ReplaceAll(metasheet, book.KeywordType, atTypeDisplacement)
 	metasheet = escapeAttrs(metasheet)
 	metasheet = xmlProlog + "\n" + metasheet
diff --git a/internal/importer/yaml.go b/internal/importer/yaml.go
index d125375..4eca394 100644
--- a/internal/importer/yaml.go
+++ b/internal/importer/yaml.go
@@ -26,7 +26,7 @@ func NewYAMLImporter(filename string, sheetNames []string, parser book.SheetPars
 	if mode == Protogen {
 		book, err = readYAMLBookWithOnlySchemaSheet(filename, parser)
 		if err != nil {
-			return nil, xerrors.Wrapf(err, "failed to read csv book: %s", filename)
+			return nil, xerrors.Wrapf(err, "failed to read yaml book: %s", filename)
 		}
 		if err := book.ParseMetaAndPurge(); err != nil {
 			return nil, xerrors.Wrapf(err, "failed to parse metasheet")
@@ -34,7 +34,7 @@ func NewYAMLImporter(filename string, sheetNames []string, parser book.SheetPars
 	} else {
 		book, err = readYAMLBook(filename, parser)
 		if err != nil {
-			return nil, xerrors.Wrapf(err, "failed to read csv book: %s", filename)
+			return nil, xerrors.Wrapf(err, "failed to read yaml book: %s", filename)
 		}
 	}
 
diff --git a/internal/protogen/exporter.go b/internal/protogen/exporter.go
index 9681d86..db82b23 100644
--- a/internal/protogen/exporter.go
+++ b/internal/protogen/exporter.go
@@ -159,6 +159,9 @@ func (x *sheetExporter) export() error {
 }
 
 func (x *sheetExporter) exportEnum() error {
+	if x.ws.Note != "" {
+		x.g.P("// ", x.ws.Note)
+	}
 	x.g.P("enum ", x.ws.Name, " {")
 	opts := &tableaupb.EnumOptions{Name: x.ws.GetOptions().GetName(), Note: x.ws.Note}
 	x.g.P("  option (tableau.etype) = {", marshalToText(opts), "};")
@@ -169,7 +172,11 @@ func (x *sheetExporter) exportEnum() error {
 			ename := x.be.gen.strcaseCtx.ToScreamingSnake(x.ws.Name) + "_INVALID"
 			x.g.P("  ", ename, " = 0;")
 		}
-		x.g.P("  ", strings.TrimSpace(field.Name), " = ", field.Number, ` [(tableau.evalue).name = "`, strings.TrimSpace(field.Alias), `"];`)
+		note := ""
+		if field.Alias != "" {
+			note = " // " + field.Alias
+		}
+		x.g.P("  ", strings.TrimSpace(field.Name), " = ", field.Number, ` [(tableau.evalue).name = "`, strings.TrimSpace(field.Alias), `"];`, note)
 	}
 	x.g.P("}")
 	if !x.isLastSheet {
@@ -228,7 +235,11 @@ func (x *sheetExporter) exportUnion() error {
 	x.g.P("    TYPE_INVALID = 0;")
 	for _, field := range x.ws.Fields {
 		ename := "TYPE_" + x.be.gen.strcaseCtx.ToScreamingSnake(field.Name)
-		x.g.P("    ", ename, " = ", field.Number, ` [(tableau.evalue).name = "`, field.Alias, `"];`)
+		note := ""
+		if field.Alias != "" {
+			note = " // " + field.Alias
+		}
+		x.g.P("    ", ename, " = ", field.Number, ` [(tableau.evalue).name = "`, field.Alias, `"];`, note)
 	}
 	x.g.P("  }")
 	x.g.P()
diff --git a/options/options.go b/options/options.go
index 20547dc..225da3d 100644
--- a/options/options.go
+++ b/options/options.go
@@ -141,6 +141,7 @@ type ProtoInputOption struct {
 	FollowSymlink bool `yaml:"followSymlink"`
 
 	// Specify metasheet name. Metasheet is "@TABLEAU" if not set.
+	// NOTE: metasheet name must start with '@'.
 	//
 	// Default: "".
 	MetasheetName string `yaml:"metasheetName"`


Test patch:
diff --git a/internal/confgen/table_parser_test.go b/internal/confgen/table_parser_test.go
index 3504db2..5f3d50f 100644
--- a/internal/confgen/table_parser_test.go
+++ b/internal/confgen/table_parser_test.go
@@ -529,25 +529,8 @@ func TestTableParser_parseHorizontalMapWithEmptyKey(t *testing.T) {
 }
 
 func TestTableParser_parseDocumentMetasheet(t *testing.T) {
-	path := "./testdata/Metasheet.yaml"
-	parser := NewExtendedSheetParser("protoconf", "Asia/Shanghai", strcase.Context{},
-		book.MetabookOptions(),
-		book.MetasheetOptions(),
-		&SheetParserExtInfo{
-			InputDir:       "",
-			SubdirRewrites: map[string]string{},
-			BookFormat:     format.YAML,
-		})
-	imp, err := importer.New(path, importer.Parser(parser))
-	if err != nil {
-		t.Fatal(err)
-	}
-	sheet := imp.GetSheet(book.MetasheetName)
-	if sheet == nil {
-		t.Fatalf("metasheet not found")
-	}
 	type args struct {
-		sheet *book.Sheet
+		path string
 	}
 	tests := []struct {
 		name    string
@@ -557,9 +540,16 @@ func TestTableParser_parseDocumentMetasheet(t *testing.T) {
 		wantMsg proto.Message
 	}{
 		{
-			name:    "parse document metasheet",
-			parser:  parser,
-			args:    args{sheet: sheet},
+			name: "parse yaml metasheet",
+			parser: NewExtendedSheetParser("protoconf", "Asia/Shanghai", strcase.Context{},
+				book.MetabookOptions(),
+				book.MetasheetOptions(),
+				&SheetParserExtInfo{
+					InputDir:       "",
+					SubdirRewrites: map[string]string{},
+					BookFormat:     format.YAML,
+				}),
+			args:    args{path: "./testdata/Metasheet.yaml"},
 			wantErr: false,
 			wantMsg: &internalpb.Metabook{
 				MetasheetMap: map[string]*internalpb.Metasheet{
@@ -574,11 +564,40 @@ func TestTableParser_parseDocumentMetasheet(t *testing.T) {
 				},
 			},
 		},
+		{
+			name: "parse xml metasheet",
+			parser: NewExtendedSheetParser("protoconf", "Asia/Shanghai", strcase.Context{},
+				book.MetabookOptions(),
+				book.MetasheetOptions(),
+				&SheetParserExtInfo{
+					InputDir:       "",
+					SubdirRewrites: map[string]string{},
+					BookFormat:     format.XML,
+				}),
+			args:    args{path: "./testdata/Metasheet.xml"},
+			wantErr: false,
+			wantMsg: &internalpb.Metabook{
+				MetasheetMap: map[string]*internalpb.Metasheet{
+					"ItemConf": {
+						Sheet:      "ItemConf",
+						OrderedMap: true,
+					},
+				},
+			},
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
+			imp, err := importer.New(tt.args.path, importer.Parser(tt.parser))
+			if err != nil {
+				t.Fatal(err)
+			}
+			sheet := imp.GetSheet(book.MetasheetName)
+			if sheet == nil {
+				t.Fatalf("metasheet not found")
+			}
 			msg := &internalpb.Metabook{}
-			err := tt.parser.Parse(msg, tt.args.sheet)
+			err = tt.parser.Parse(msg, sheet)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("sheetParser.Parse() error = %s, wantErr %v", xerrors.NewDesc(err), tt.wantErr)
 			}
diff --git a/internal/confgen/testdata/Metasheet.xml b/internal/confgen/testdata/Metasheet.xml
new file mode 100644
index 0000000..c6be70f
--- /dev/null
+++ b/internal/confgen/testdata/Metasheet.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+<@TABLEAU_CLIENT>
+    <Item Sheet="ItemConf" Optional="true"/>
+</@TABLEAU_CLIENT>
+
+<@TABLEAU>
+    <Item Sheet="ItemConf" OrderedMap="true"/>
+</@TABLEAU>
+-->
diff --git a/internal/importer/book/sheet_test.go b/internal/importer/book/sheet_test.go
index 91b5466..0651382 100644
--- a/internal/importer/book/sheet_test.go
+++ b/internal/importer/book/sheet_test.go
@@ -4,6 +4,7 @@ import (
 	"reflect"
 	"testing"
 
+	"github.com/stretchr/testify/assert"
 	"github.com/tableauio/tableau/proto/tableaupb"
 	"github.com/tableauio/tableau/proto/tableaupb/internalpb"
 )
@@ -239,3 +240,9 @@ func TestSheet_ToWorkseet(t *testing.T) {
 		})
 	}
 }
+
+func TestSetMetasheetName(t *testing.T) {
+	assert.Panicsf(t, assert.PanicTestFunc(func() {
+		SetMetasheetName("No@StartMetasheetName")
+	}), "SetMetasheetName() should panic when metasheet name not starts with '@'")
+}
diff --git a/internal/importer/testdata/InvalidMeta1.xml b/internal/importer/testdata/InvalidMeta1.xml
new file mode 100644
index 0000000..cdb8aa8
--- /dev/null
+++ b/internal/importer/testdata/InvalidMeta1.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+<@TABLEAU>
+    <Item Sheet="Item" Merger="Test_*.xml"/>
+</@TABLEAU>
+
+<Item>
+    <Predefined @type="[.Item]">
+        <Scalar>int32</Scalar>
+    </Predefined>
+</Item>
+-->
diff --git a/internal/importer/testdata/InvalidMeta2.xml b/internal/importer/testdata/InvalidMeta2.xml
new file mode 100644
index 0000000..09bd46a
--- /dev/null
+++ b/internal/importer/testdata/InvalidMeta2.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+<@TABLEAU>
+    <Item Sheet="Item" Merger="Test_*.xml"/>
+</@TABLEAU>
+
+<@Item>
+    <@Scalar>int32</@Scalar>
+</Item>
+-->
diff --git a/internal/importer/testdata/NotSupportAliasNode.yaml b/internal/importer/testdata/NotSupportAliasNode.yaml
new file mode 100644
index 0000000..5472393
--- /dev/null
+++ b/internal/importer/testdata/NotSupportAliasNode.yaml
@@ -0,0 +1,10 @@
+"@sheet": "@TABLEAU"
+YamlScalarConf:
+---
+"@sheet": "@YamlScalarConf"
+First occurrence: &anchor Foo
+Second occurrence: *anchor
+---
+"@sheet": YamlScalarConf
+First occurrence: &anchor Foo
+Second occurrence: *anchor
diff --git a/internal/importer/xml_test.go b/internal/importer/xml_test.go
index 9168f00..cece012 100644
--- a/internal/importer/xml_test.go
+++ b/internal/importer/xml_test.go
@@ -68,6 +68,15 @@ func TestNewXMLImporter(t *testing.T) {
 		want    *XMLImporter
 		wantErr bool
 	}{
+		{
+			name: "not-exist",
+			args: args{
+				filename: "testdata/not-exist.xml",
+				mode:     Protogen,
+				parser:   &TestSheetParser{},
+			},
+			wantErr: true,
+		},
 		{
 			name: "Test.xml",
 			args: args{
@@ -76,15 +85,43 @@ func TestNewXMLImporter(t *testing.T) {
 				parser:   &TestSheetParser{},
 			},
 		},
+		{
+			name: "InvalidMeta1.xml",
+			args: args{
+				filename: "testdata/InvalidMeta1.xml",
+				mode:     Protogen,
+				parser:   &TestSheetParser{},
+			},
+			wantErr: true,
+		},
+		{
+			name: "InvalidMeta2.xml",
+			args: args{
+				filename: "testdata/InvalidMeta2.xml",
+				mode:     Protogen,
+				parser:   &TestSheetParser{},
+			},
+			wantErr: true,
+		},
+		{
+			name: "InvalidMeta2.xml",
+			args: args{
+				filename: "testdata/InvalidMeta2.xml",
+				parser:   &TestSheetParser{},
+			},
+			wantErr: true,
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			got, err := NewXMLImporter(tt.args.filename, tt.args.sheetNames, tt.args.parser, tt.args.mode, tt.args.cloned)
 			if (err != nil) != tt.wantErr {
-				t.Errorf("NewYAMLImporter() error = %v, wantErr %v", err, tt.wantErr)
+				t.Errorf("NewXMLImporter() error = %v, wantErr %v", err, tt.wantErr)
 				return
 			}
-			fmt.Println(got.String())
+			if got != nil {
+				fmt.Println(got.String())
+			}
 		})
 	}
 }
diff --git a/internal/importer/yaml_test.go b/internal/importer/yaml_test.go
index 5e33699..09c4b39 100644
--- a/internal/importer/yaml_test.go
+++ b/internal/importer/yaml_test.go
@@ -85,6 +85,31 @@ func TestNewYAMLImporter(t *testing.T) {
 				parser:   &TestSheetParser{},
 			},
 		},
+		{
+			name: "not-exist.yaml",
+			args: args{
+				filename: "testdata/not-exist.yaml",
+				parser:   nil,
+			},
+			wantErr: true,
+		},
+		{
+			name: "NotSupportAliasNode.yaml",
+			args: args{
+				filename: "testdata/NotSupportAliasNode.yaml",
+				mode:     Protogen,
+				parser:   &TestSheetParser{},
+			},
+			wantErr: true,
+		},
+		{
+			name: "NotSupportAliasNode.yaml",
+			args: args{
+				filename: "testdata/NotSupportAliasNode.yaml",
+				parser:   &TestSheetParser{},
+			},
+			wantErr: true,
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -93,10 +118,9 @@ func TestNewYAMLImporter(t *testing.T) {
 				t.Errorf("NewYAMLImporter() error = %v, wantErr %v", err, tt.wantErr)
 				return
 			}
-			fmt.Println(got.String())
-			// if !reflect.DeepEqual(got, tt.want) {
-			// 	t.Errorf("NewYAMLImporter() = %v, want %v", got, tt.want)
-			// }
+			if got != nil {
+				fmt.Println(got.String())
+			}
 		})
 	}
 }
diff --git a/internal/protogen/exporter_test.go b/internal/protogen/exporter_test.go
index cd94341..202f21f 100644
--- a/internal/protogen/exporter_test.go
+++ b/internal/protogen/exporter_test.go
@@ -279,9 +279,9 @@ func Test_sheetExporter_exportEnum(t *testing.T) {
   option (tableau.etype) = {name:"ItemType"};
 
   ITEM_TYPE_INVALID = 0;
-  ITEM_TYPE_FRUIT = 1 [(tableau.evalue).name = "Fruit"];
-  ITEM_TYPE_EQUIP = 2 [(tableau.evalue).name = "Equip"];
-  ITEM_TYPE_BOX = 3 [(tableau.evalue).name = "Box"];
+  ITEM_TYPE_FRUIT = 1 [(tableau.evalue).name = "Fruit"]; // Fruit
+  ITEM_TYPE_EQUIP = 2 [(tableau.evalue).name = "Equip"]; // Equip
+  ITEM_TYPE_BOX = 3 [(tableau.evalue).name = "Box"]; // Box
 }
 
 `,
@@ -392,7 +392,7 @@ func Test_sheetExporter_exportUnion(t *testing.T) {
 
   enum Type {
     TYPE_INVALID = 0;
-    TYPE_PVP_BATTLE = 1 [(tableau.evalue).name = "SoloPVPBattle"];
+    TYPE_PVP_BATTLE = 1 [(tableau.evalue).name = "SoloPVPBattle"]; // SoloPVPBattle
   }
 
   message PvpBattle {
diff --git a/test/functest/proto/custom/excel__header__header.proto b/test/functest/proto/custom/excel__header__header.proto
index db4549d..9135b27 100644
--- a/test/functest/proto/custom/excel__header__header.proto
+++ b/test/functest/proto/custom/excel__header__header.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/excel__dev__hero__hero.proto b/test/functest/proto/default/excel__dev__hero__hero.proto
index 6cbfe9d..5c7044f 100644
--- a/test/functest/proto/default/excel__dev__hero__hero.proto
+++ b/test/functest/proto/default/excel__dev__hero__hero.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/excel__dev__list__list.proto b/test/functest/proto/default/excel__dev__list__list.proto
index 19dbb07..4c1c009 100644
--- a/test/functest/proto/default/excel__dev__list__list.proto
+++ b/test/functest/proto/default/excel__dev__list__list.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/excel__dev__test.proto b/test/functest/proto/default/excel__dev__test.proto
index db98a91..726a7d9 100644
--- a/test/functest/proto/default/excel__dev__test.proto
+++ b/test/functest/proto/default/excel__dev__test.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/excel__fieldprop__field_prop.proto b/test/functest/proto/default/excel__fieldprop__field_prop.proto
index 1c73a61..9b2e64d 100644
--- a/test/functest/proto/default/excel__fieldprop__field_prop.proto
+++ b/test/functest/proto/default/excel__fieldprop__field_prop.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/excel__keyedlist__keyed_list.proto b/test/functest/proto/default/excel__keyedlist__keyed_list.proto
index b5d1cde..39aba17 100644
--- a/test/functest/proto/default/excel__keyedlist__keyed_list.proto
+++ b/test/functest/proto/default/excel__keyedlist__keyed_list.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/excel__keyedlist__vertical_keyed_list.proto b/test/functest/proto/default/excel__keyedlist__vertical_keyed_list.proto
index 01ff8bd..3e3f977 100644
--- a/test/functest/proto/default/excel__keyedlist__vertical_keyed_list.proto
+++ b/test/functest/proto/default/excel__keyedlist__vertical_keyed_list.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/excel__list__horizontal_list.proto b/test/functest/proto/default/excel__list__horizontal_list.proto
index c487b48..bc1e290 100644
--- a/test/functest/proto/default/excel__list__horizontal_list.proto
+++ b/test/functest/proto/default/excel__list__horizontal_list.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/excel__list__list.proto b/test/functest/proto/default/excel__list__list.proto
index 1a96f66..95f3ce7 100644
--- a/test/functest/proto/default/excel__list__list.proto
+++ b/test/functest/proto/default/excel__list__list.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/excel__list__vertical_list.proto b/test/functest/proto/default/excel__list__vertical_list.proto
index d12f398..5c727ac 100644
--- a/test/functest/proto/default/excel__list__vertical_list.proto
+++ b/test/functest/proto/default/excel__list__vertical_list.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/excel__map__horizontal_map.proto b/test/functest/proto/default/excel__map__horizontal_map.proto
index e24ee4b..e0ba376 100644
--- a/test/functest/proto/default/excel__map__horizontal_map.proto
+++ b/test/functest/proto/default/excel__map__horizontal_map.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/excel__map__map.proto b/test/functest/proto/default/excel__map__map.proto
index 519e55a..c50fb16 100644
--- a/test/functest/proto/default/excel__map__map.proto
+++ b/test/functest/proto/default/excel__map__map.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/excel__map__vertical_map.proto b/test/functest/proto/default/excel__map__vertical_map.proto
index 957c09b..5f57637 100644
--- a/test/functest/proto/default/excel__map__vertical_map.proto
+++ b/test/functest/proto/default/excel__map__vertical_map.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/excel__metasheet__aliased_workbook.proto b/test/functest/proto/default/excel__metasheet__aliased_workbook.proto
index 63b13eb..729042e 100644
--- a/test/functest/proto/default/excel__metasheet__aliased_workbook.proto
+++ b/test/functest/proto/default/excel__metasheet__aliased_workbook.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/excel__metasheet__merger_1.proto b/test/functest/proto/default/excel__metasheet__merger_1.proto
index ba257ba..25eb6c3 100644
--- a/test/functest/proto/default/excel__metasheet__merger_1.proto
+++ b/test/functest/proto/default/excel__metasheet__merger_1.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/excel__metasheet__metasheet.proto b/test/functest/proto/default/excel__metasheet__metasheet.proto
index 837f7df..09d9666 100644
--- a/test/functest/proto/default/excel__metasheet__metasheet.proto
+++ b/test/functest/proto/default/excel__metasheet__metasheet.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/excel__metasheet__scatter_1.proto b/test/functest/proto/default/excel__metasheet__scatter_1.proto
index c6046b8..8b71c10 100644
--- a/test/functest/proto/default/excel__metasheet__scatter_1.proto
+++ b/test/functest/proto/default/excel__metasheet__scatter_1.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/excel__metasheet__sheet_mode.proto b/test/functest/proto/default/excel__metasheet__sheet_mode.proto
index 3158ea4..83c8011 100644
--- a/test/functest/proto/default/excel__metasheet__sheet_mode.proto
+++ b/test/functest/proto/default/excel__metasheet__sheet_mode.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
@@ -13,31 +13,34 @@ import "tableau/protobuf/tableau.proto";
 option go_package = "github.com/tableauio/tableau/test/functest/protoconf";
 option (tableau.workbook) = {name:"excel/metasheet/SheetMode#*.csv" namerow:1 typerow:2 noterow:3 datarow:4 sep:"," subsep:":"};
 
+// CatType note
 enum CatType {
   option (tableau.etype) = {name:"EnumType" note:"CatType note"};
 
   CAT_TYPE_INVALID = 0;
-  CAT_TYPE_RAGDOLL = 1 [(tableau.evalue).name = "Ragdoll"];
-  CAT_TYPE_PERSIAN = 2 [(tableau.evalue).name = "Persian"];
-  CAT_TYPE_SPHYNX = 3 [(tableau.evalue).name = "Sphynx"];
+  CAT_TYPE_RAGDOLL = 1 [(tableau.evalue).name = "Ragdoll"]; // Ragdoll
+  CAT_TYPE_PERSIAN = 2 [(tableau.evalue).name = "Persian"]; // Persian
+  CAT_TYPE_SPHYNX = 3 [(tableau.evalue).name = "Sphynx"]; // Sphynx
 }
 
+// DogType note
 enum DogType {
   option (tableau.etype) = {name:"EnumType" note:"DogType note"};
 
   DOG_TYPE_INVALID = 0;
-  DOG_TYPE_POODLE = 1 [(tableau.evalue).name = "Poodle"];
-  DOG_TYPE_BULLDOG = 2 [(tableau.evalue).name = "Bulldog"];
-  DOG_TYPE_DACHSHUND = 3 [(tableau.evalue).name = "Dachshund"];
+  DOG_TYPE_POODLE = 1 [(tableau.evalue).name = "Poodle"]; // Poodle
+  DOG_TYPE_BULLDOG = 2 [(tableau.evalue).name = "Bulldog"]; // Bulldog
+  DOG_TYPE_DACHSHUND = 3 [(tableau.evalue).name = "Dachshund"]; // Dachshund
 }
 
+// BirdType note
 enum BirdType {
   option (tableau.etype) = {name:"EnumType" note:"BirdType note"};
 
   BIRD_TYPE_INVALID = 0;
-  BIRD_TYPE_CANARY = 1 [(tableau.evalue).name = "Canary"];
-  BIRD_TYPE_WOODPECKER = 2 [(tableau.evalue).name = "Woodpecker"];
-  BIRD_TYPE_OWL = 3 [(tableau.evalue).name = "Owl"];
+  BIRD_TYPE_CANARY = 1 [(tableau.evalue).name = "Canary"]; // Canary
+  BIRD_TYPE_WOODPECKER = 2 [(tableau.evalue).name = "Woodpecker"]; // Woodpecker
+  BIRD_TYPE_OWL = 3 [(tableau.evalue).name = "Owl"]; // Owl
 }
 
 message EnumTypeReuseInBook {
@@ -56,9 +59,9 @@ enum ItemType {
   option (tableau.etype) = {name:"ItemType"};
 
   ITEM_TYPE_INVALID = 0;
-  ITEM_TYPE_FRUIT = 1 [(tableau.evalue).name = "Fruit"];
-  ITEM_TYPE_EQUIP = 2 [(tableau.evalue).name = "Equip"];
-  ITEM_TYPE_BOX = 3 [(tableau.evalue).name = "Box"];
+  ITEM_TYPE_FRUIT = 1 [(tableau.evalue).name = "Fruit"]; // Fruit
+  ITEM_TYPE_EQUIP = 2 [(tableau.evalue).name = "Equip"]; // Equip
+  ITEM_TYPE_BOX = 3 [(tableau.evalue).name = "Box"]; // Box
 }
 
 message TaskReward {
@@ -123,9 +126,9 @@ message SimpleTarget {
 
   enum Type {
     TYPE_INVALID = 0;
-    TYPE_PVP = 1 [(tableau.evalue).name = "AliasPVP"];
-    TYPE_PVE = 2 [(tableau.evalue).name = "AliasPVE"];
-    TYPE_SKILL = 3 [(tableau.evalue).name = "AliasSkill"];
+    TYPE_PVP = 1 [(tableau.evalue).name = "AliasPVP"]; // AliasPVP
+    TYPE_PVE = 2 [(tableau.evalue).name = "AliasPVE"]; // AliasPVE
+    TYPE_SKILL = 3 [(tableau.evalue).name = "AliasSkill"]; // AliasSkill
   }
 
   message PVP {
@@ -160,12 +163,12 @@ message TaskTarget {
 
   enum Type {
     TYPE_INVALID = 0;
-    TYPE_PVP = 1 [(tableau.evalue).name = "AliasPVP"];
-    TYPE_PVE = 2 [(tableau.evalue).name = "AliasPVE"];
-    TYPE_STORY = 3 [(tableau.evalue).name = "AliasStory"];
-    TYPE_HOBBY = 4 [(tableau.evalue).name = "AliasHobby"];
-    TYPE_SKILL = 5 [(tableau.evalue).name = "AliasSkill"];
-    TYPE_EMPTY = 6 [(tableau.evalue).name = "AliasEmpty"];
+    TYPE_PVP = 1 [(tableau.evalue).name = "AliasPVP"]; // AliasPVP
+    TYPE_PVE = 2 [(tableau.evalue).name = "AliasPVE"]; // AliasPVE
+    TYPE_STORY = 3 [(tableau.evalue).name = "AliasStory"]; // AliasStory
+    TYPE_HOBBY = 4 [(tableau.evalue).name = "AliasHobby"]; // AliasHobby
+    TYPE_SKILL = 5 [(tableau.evalue).name = "AliasSkill"]; // AliasSkill
+    TYPE_EMPTY = 6 [(tableau.evalue).name = "AliasEmpty"]; // AliasEmpty
   }
 
   message PVP {
@@ -219,8 +222,8 @@ message WishTarget {
 
   enum Type {
     TYPE_INVALID = 0;
-    TYPE_HIGHER = 1 [(tableau.evalue).name = "WishHigher"];
-    TYPE_RICHER = 2 [(tableau.evalue).name = "WishRicher"];
+    TYPE_HIGHER = 1 [(tableau.evalue).name = "WishHigher"]; // WishHigher
+    TYPE_RICHER = 2 [(tableau.evalue).name = "WishRicher"]; // WishRicher
   }
 
   message Higher {
@@ -245,8 +248,8 @@ message HeroTarget {
 
   enum Type {
     TYPE_INVALID = 0;
-    TYPE_STAR_UP = 1 [(tableau.evalue).name = "HeroStarUp"];
-    TYPE_LEVEL_UP = 2 [(tableau.evalue).name = "HeroLevelUp"];
+    TYPE_STAR_UP = 1 [(tableau.evalue).name = "HeroStarUp"]; // HeroStarUp
+    TYPE_LEVEL_UP = 2 [(tableau.evalue).name = "HeroLevelUp"]; // HeroLevelUp
   }
 
   message StarUp {
@@ -273,8 +276,8 @@ message BattleTarget {
 
   enum Type {
     TYPE_INVALID = 0;
-    TYPE_PVP = 1 [(tableau.evalue).name = "BattlePVP"];
-    TYPE_PVE = 2 [(tableau.evalue).name = "BattlePVE"];
+    TYPE_PVP = 1 [(tableau.evalue).name = "BattlePVP"]; // BattlePVP
+    TYPE_PVE = 2 [(tableau.evalue).name = "BattlePVE"]; // BattlePVE
   }
 
   message PVP {
diff --git a/test/functest/proto/default/excel__misc__note.proto b/test/functest/proto/default/excel__misc__note.proto
index 4e3e712..9e65255 100644
--- a/test/functest/proto/default/excel__misc__note.proto
+++ b/test/functest/proto/default/excel__misc__note.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/excel__misc__type_reuse.proto b/test/functest/proto/default/excel__misc__type_reuse.proto
index 0610fbc..18c12ba 100644
--- a/test/functest/proto/default/excel__misc__type_reuse.proto
+++ b/test/functest/proto/default/excel__misc__type_reuse.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/excel__nesting__keyed_list_in_keyed_list.proto b/test/functest/proto/default/excel__nesting__keyed_list_in_keyed_list.proto
index aa79f01..462123b 100644
--- a/test/functest/proto/default/excel__nesting__keyed_list_in_keyed_list.proto
+++ b/test/functest/proto/default/excel__nesting__keyed_list_in_keyed_list.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/excel__nesting__list_in_list.proto b/test/functest/proto/default/excel__nesting__list_in_list.proto
index ff09bc4..609be0e 100644
--- a/test/functest/proto/default/excel__nesting__list_in_list.proto
+++ b/test/functest/proto/default/excel__nesting__list_in_list.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/excel__nesting__list_in_map.proto b/test/functest/proto/default/excel__nesting__list_in_map.proto
index 7ffe14e..0dccc15 100644
--- a/test/functest/proto/default/excel__nesting__list_in_map.proto
+++ b/test/functest/proto/default/excel__nesting__list_in_map.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/excel__nesting__map_in_list.proto b/test/functest/proto/default/excel__nesting__map_in_list.proto
index 5b8dc12..2a9aa66 100644
--- a/test/functest/proto/default/excel__nesting__map_in_list.proto
+++ b/test/functest/proto/default/excel__nesting__map_in_list.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/excel__nesting__map_in_map.proto b/test/functest/proto/default/excel__nesting__map_in_map.proto
index caa704b..3d7f2b3 100644
--- a/test/functest/proto/default/excel__nesting__map_in_map.proto
+++ b/test/functest/proto/default/excel__nesting__map_in_map.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/excel__nesting__struct_in_list.proto b/test/functest/proto/default/excel__nesting__struct_in_list.proto
index dd98fae..3cfd2fa 100644
--- a/test/functest/proto/default/excel__nesting__struct_in_list.proto
+++ b/test/functest/proto/default/excel__nesting__struct_in_list.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/excel__nesting__struct_in_map.proto b/test/functest/proto/default/excel__nesting__struct_in_map.proto
index 3091ac3..2c083c5 100644
--- a/test/functest/proto/default/excel__nesting__struct_in_map.proto
+++ b/test/functest/proto/default/excel__nesting__struct_in_map.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/excel__nesting__struct_in_struct.proto b/test/functest/proto/default/excel__nesting__struct_in_struct.proto
index e8193ce..59677f3 100644
--- a/test/functest/proto/default/excel__nesting__struct_in_struct.proto
+++ b/test/functest/proto/default/excel__nesting__struct_in_struct.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/excel__scalar__scalar.proto b/test/functest/proto/default/excel__scalar__scalar.proto
index c6331f2..1a4d511 100644
--- a/test/functest/proto/default/excel__scalar__scalar.proto
+++ b/test/functest/proto/default/excel__scalar__scalar.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/excel__struct__struct.proto b/test/functest/proto/default/excel__struct__struct.proto
index 8927210..c71f21b 100644
--- a/test/functest/proto/default/excel__struct__struct.proto
+++ b/test/functest/proto/default/excel__struct__struct.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/excel__union__union.proto b/test/functest/proto/default/excel__union__union.proto
index 4d3d93a..da6e7e2 100644
--- a/test/functest/proto/default/excel__union__union.proto
+++ b/test/functest/proto/default/excel__union__union.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
@@ -60,10 +60,10 @@ message FieldCapTarget {
 
   enum Type {
     TYPE_INVALID = 0;
-    TYPE_EXCHANGE = 101 [(tableau.evalue).name = "Exchange"];
-    TYPE_VOTE = 102 [(tableau.evalue).name = "Vote"];
-    TYPE_FRUIT_2V2 = 103 [(tableau.evalue).name = "Fruit"];
-    TYPE_KEY_3V3 = 104 [(tableau.evalue).name = "Key"];
+    TYPE_EXCHANGE = 101 [(tableau.evalue).name = "Exchange"]; // Exchange
+    TYPE_VOTE = 102 [(tableau.evalue).name = "Vote"]; // Vote
+    TYPE_FRUIT_2V2 = 103 [(tableau.evalue).name = "Fruit"]; // Fruit
+    TYPE_KEY_3V3 = 104 [(tableau.evalue).name = "Key"]; // Key
   }
 
   message Exchange {
diff --git a/test/functest/proto/default/excel__wellknown__well_known.proto b/test/functest/proto/default/excel__wellknown__well_known.proto
index 5fcb83b..21b20f1 100644
--- a/test/functest/proto/default/excel__wellknown__well_known.proto
+++ b/test/functest/proto/default/excel__wellknown__well_known.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/xml__list__list.proto b/test/functest/proto/default/xml__list__list.proto
index cde4300..acc5ea1 100644
--- a/test/functest/proto/default/xml__list__list.proto
+++ b/test/functest/proto/default/xml__list__list.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/xml__map__map.proto b/test/functest/proto/default/xml__map__map.proto
index 27eb95d..0a93a2d 100644
--- a/test/functest/proto/default/xml__map__map.proto
+++ b/test/functest/proto/default/xml__map__map.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/xml__metasheet__merger.proto b/test/functest/proto/default/xml__metasheet__merger.proto
index 3208bc1..590cbbc 100644
--- a/test/functest/proto/default/xml__metasheet__merger.proto
+++ b/test/functest/proto/default/xml__metasheet__merger.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/xml__metasheet__metasheet.proto b/test/functest/proto/default/xml__metasheet__metasheet.proto
index 5bcefd4..876f59c 100644
--- a/test/functest/proto/default/xml__metasheet__metasheet.proto
+++ b/test/functest/proto/default/xml__metasheet__metasheet.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/xml__patch__base__env.proto b/test/functest/proto/default/xml__patch__base__env.proto
index 53ce972..9d8ea60 100644
--- a/test/functest/proto/default/xml__patch__base__env.proto
+++ b/test/functest/proto/default/xml__patch__base__env.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/xml__struct.proto b/test/functest/proto/default/xml__struct.proto
index 8cf3c95..0dfa9c9 100644
--- a/test/functest/proto/default/xml__struct.proto
+++ b/test/functest/proto/default/xml__struct.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/xml__union.proto b/test/functest/proto/default/xml__union.proto
index f05b64b..38ea287 100644
--- a/test/functest/proto/default/xml__union.proto
+++ b/test/functest/proto/default/xml__union.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/yaml__enum.proto b/test/functest/proto/default/yaml__enum.proto
index 7189d25..7324bbd 100644
--- a/test/functest/proto/default/yaml__enum.proto
+++ b/test/functest/proto/default/yaml__enum.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/yaml__fieldprop__optional.proto b/test/functest/proto/default/yaml__fieldprop__optional.proto
index e0b05d4..a4c2bbc 100644
--- a/test/functest/proto/default/yaml__fieldprop__optional.proto
+++ b/test/functest/proto/default/yaml__fieldprop__optional.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/yaml__list.proto b/test/functest/proto/default/yaml__list.proto
index 7223127..f6a4c66 100644
--- a/test/functest/proto/default/yaml__list.proto
+++ b/test/functest/proto/default/yaml__list.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/yaml__map.proto b/test/functest/proto/default/yaml__map.proto
index 2aaeb78..b96e18d 100644
--- a/test/functest/proto/default/yaml__map.proto
+++ b/test/functest/proto/default/yaml__map.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/yaml__metasheet__merger.proto b/test/functest/proto/default/yaml__metasheet__merger.proto
index ea51c99..03f45d3 100644
--- a/test/functest/proto/default/yaml__metasheet__merger.proto
+++ b/test/functest/proto/default/yaml__metasheet__merger.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/yaml__metasheet__yaml_scatter.proto b/test/functest/proto/default/yaml__metasheet__yaml_scatter.proto
index 4bf1d44..ccd39ac 100644
--- a/test/functest/proto/default/yaml__metasheet__yaml_scatter.proto
+++ b/test/functest/proto/default/yaml__metasheet__yaml_scatter.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/yaml__misc__acronym.proto b/test/functest/proto/default/yaml__misc__acronym.proto
index 6744c1e..5a13fa2 100644
--- a/test/functest/proto/default/yaml__misc__acronym.proto
+++ b/test/functest/proto/default/yaml__misc__acronym.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/yaml__misc__special_value.proto b/test/functest/proto/default/yaml__misc__special_value.proto
index f2a13e3..b0f3cb6 100644
--- a/test/functest/proto/default/yaml__misc__special_value.proto
+++ b/test/functest/proto/default/yaml__misc__special_value.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/yaml__patch__base__env.proto b/test/functest/proto/default/yaml__patch__base__env.proto
index 84af230..a0dbfac 100644
--- a/test/functest/proto/default/yaml__patch__base__env.proto
+++ b/test/functest/proto/default/yaml__patch__base__env.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/yaml__scalar.proto b/test/functest/proto/default/yaml__scalar.proto
index 834d872..c83128c 100644
--- a/test/functest/proto/default/yaml__scalar.proto
+++ b/test/functest/proto/default/yaml__scalar.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/yaml__struct.proto b/test/functest/proto/default/yaml__struct.proto
index bc7cf69..857827c 100644
--- a/test/functest/proto/default/yaml__struct.proto
+++ b/test/functest/proto/default/yaml__struct.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";
diff --git a/test/functest/proto/default/yaml__union.proto b/test/functest/proto/default/yaml__union.proto
index c36f7a5..9ad0a12 100644
--- a/test/functest/proto/default/yaml__union.proto
+++ b/test/functest/proto/default/yaml__union.proto
@@ -1,4 +1,4 @@
-// Code generated by tableau (protogen v0.7.1). DO NOT EDIT.
+// Code generated by tableau (protogen v0.8.0). DO NOT EDIT.
 // clang-format off
 
 syntax = "proto3";