Repository: yorkie-team/yorkie
Pull request #579 (2023-07-12 01:54:26)

Allow users to pass multi nodes when calling Tree.edit
<!--  Thanks for sending a pull request! -->

**What this PR does / why we need it**:
- allow users to pass multi tree nodes when edit
   - change parameter type `*TreeNode` => `[]*TreeNode`
     - modify json/tree, crdt/tree and converter
 - remove duplicate code
 - update TC

FYI
- most of the lines that have changed are tc, 
- please review 
**Which issue(s) this PR fixes**:
<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
-->
Fixes #

**Special notes for your reviewer**:

**Does this PR introduce a user-facing change?**:
<!--
If no, just write "NONE" in the release-note block below.
If yes, a release note is required:
Enter your extended release note in the block below. If the PR requires additional action from users switching to the new release, include the string "action required".
-->
```release-note

```

**Additional documentation**:

<!--
This section can be blank if this pull request does not require a release note.

Please use the following format for linking documentation:
- [Usage]: <link>
- [Other doc]: <link>
-->
```docs

```

**Checklist**:
- [ ] Added relevant tests or not required
- [ ] Didn't break anything


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/yorkie-team/yorkie/pull/579?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team) Report
> Merging [#579](https://app.codecov.io/gh/yorkie-team/yorkie/pull/579?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team) (94bc5e5) into [main](https://app.codecov.io/gh/yorkie-team/yorkie/commit/2b15ac5423b6c5021bb5d81581347a519437ac9c?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team) (2b15ac5) will **decrease** coverage by `0.10%`.
> The diff coverage is `35.13%`.

```diff
@@            Coverage Diff             @@
##             main     #579      +/-   ##
==========================================
- Coverage   51.50%   51.40%   -0.10%     
==========================================
  Files          67       67              
  Lines        6932     6951      +19     
==========================================
+ Hits         3570     3573       +3     
- Misses       2891     2907      +16     
  Partials      471      471              
```


| [Impacted Files](https://app.codecov.io/gh/yorkie-team/yorkie/pull/579?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team) | Coverage Δ | |
|---|---|---|
| [api/converter/from\_pb.go](https://app.codecov.io/gh/yorkie-team/yorkie/pull/579?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-YXBpL2NvbnZlcnRlci9mcm9tX3BiLmdv) | `48.20% <0.00%> (-0.75%)` | :arrow_down: |
| [api/converter/to\_bytes.go](https://app.codecov.io/gh/yorkie-team/yorkie/pull/579?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-YXBpL2NvbnZlcnRlci90b19ieXRlcy5nbw==) | `68.58% <0.00%> (-3.71%)` | :arrow_down: |
| [api/converter/to\_pb.go](https://app.codecov.io/gh/yorkie-team/yorkie/pull/579?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-YXBpL2NvbnZlcnRlci90b19wYi5nbw==) | `57.27% <0.00%> (ø)` | |
| [pkg/document/crdt/tree.go](https://app.codecov.io/gh/yorkie-team/yorkie/pull/579?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-cGtnL2RvY3VtZW50L2NyZHQvdHJlZS5nbw==) | `53.68% <72.22%> (+0.43%)` | :arrow_up: |



Patch:
diff --git a/api/converter/from_pb.go b/api/converter/from_pb.go
index 0e12ca10..deee8b5f 100644
--- a/api/converter/from_pb.go
+++ b/api/converter/from_pb.go
@@ -541,7 +541,7 @@ func fromTreeEdit(pbTreeEdit *api.Operation_TreeEdit) (*operations.TreeEdit, err
 		return nil, err
 	}
 
-	node, err := FromTreeNodes(pbTreeEdit.Content)
+	nodes, err := FromTreeNodesWhenEdit(pbTreeEdit.Contents)
 	if err != nil {
 		return nil, err
 	}
@@ -550,7 +550,7 @@ func fromTreeEdit(pbTreeEdit *api.Operation_TreeEdit) (*operations.TreeEdit, err
 		parentCreatedAt,
 		from,
 		to,
-		node,
+		nodes,
 		executedAt,
 	), nil
 }
@@ -647,6 +647,28 @@ func FromTreeNodes(pbNodes []*api.TreeNode) (*crdt.TreeNode, error) {
 	return crdt.NewTree(root, nil).Root(), nil
 }
 
+// FromTreeNodesWhenEdit converts protobuf tree nodes to array of crdt.TreeNode.
+// in each element in array, the last node in slice is the root node, because the slice is in post-order.
+func FromTreeNodesWhenEdit(pbNodes []*api.TreeNodes) ([]*crdt.TreeNode, error) {
+	if len(pbNodes) == 0 {
+		return nil, nil
+	}
+
+	var treeNodes []*crdt.TreeNode
+
+	for _, pbNode := range pbNodes {
+		treeNode, err := FromTreeNodes(pbNode.Content)
+
+		if err != nil {
+			return nil, err
+		}
+
+		treeNodes = append(treeNodes, treeNode)
+	}
+
+	return treeNodes, nil
+}
+
 func fromTreeNode(pbNode *api.TreeNode) (*crdt.TreeNode, error) {
 	pos, err := fromTreePos(pbNode.Pos)
 	if err != nil {
diff --git a/api/converter/to_bytes.go b/api/converter/to_bytes.go
index f6b5b08e..fdd7de5f 100644
--- a/api/converter/to_bytes.go
+++ b/api/converter/to_bytes.go
@@ -240,6 +240,26 @@ func ToTreeNodes(treeNode *crdt.TreeNode) []*api.TreeNode {
 	return pbTreeNodes
 }
 
+// ToTreeNodesWhenEdit converts a TreeNodes to a slice of two-dimensional array of TreeNodes in post-order traversal.
+func ToTreeNodesWhenEdit(treeNodes []*crdt.TreeNode) []*api.TreeNodes {
+	pbTreeNodes := make([]*api.TreeNodes, len(treeNodes))
+
+	if len(treeNodes) == 0 {
+		return pbTreeNodes
+	}
+
+	for i, treeNode := range treeNodes {
+		var pbTreeNode []*api.TreeNode
+
+		pbTreeNode = append(pbTreeNode, ToTreeNodes(treeNode)[:]...)
+
+		pbTreeNodes[i] = &api.TreeNodes{}
+		pbTreeNodes[i].Content = append(pbTreeNodes[i].Content, pbTreeNode[:]...)
+	}
+
+	return pbTreeNodes
+}
+
 func toTreeNode(treeNode *crdt.TreeNode, depth int) *api.TreeNode {
 	var attrs map[string]*api.NodeAttr
 	if treeNode.Attrs != nil {
diff --git a/api/converter/to_pb.go b/api/converter/to_pb.go
index 350f3ed8..be40e327 100644
--- a/api/converter/to_pb.go
+++ b/api/converter/to_pb.go
@@ -395,7 +395,7 @@ func toTreeEdit(e *operations.TreeEdit) (*api.Operation_TreeEdit_, error) {
 			ParentCreatedAt: ToTimeTicket(e.ParentCreatedAt()),
 			From:            toTreePos(e.FromPos()),
 			To:              toTreePos(e.ToPos()),
-			Content:         ToTreeNodes(e.Content()),
+			Contents:        ToTreeNodesWhenEdit(e.Contents()),
 			ExecutedAt:      ToTimeTicket(e.ExecutedAt()),
 		},
 	}, nil
diff --git a/api/yorkie/v1/resources.pb.go b/api/yorkie/v1/resources.pb.go
index 177adde8..b33c3167 100644
--- a/api/yorkie/v1/resources.pb.go
+++ b/api/yorkie/v1/resources.pb.go
@@ -1116,14 +1116,14 @@ func (m *Operation_Increase) GetExecutedAt() *TimeTicket {
 }
 
 type Operation_TreeEdit struct {
-	ParentCreatedAt      *TimeTicket `protobuf:"bytes,1,opt,name=parent_created_at,json=parentCreatedAt,proto3" json:"parent_created_at,omitempty"`
-	From                 *TreePos    `protobuf:"bytes,2,opt,name=from,proto3" json:"from,omitempty"`
-	To                   *TreePos    `protobuf:"bytes,3,opt,name=to,proto3" json:"to,omitempty"`
-	Content              []*TreeNode `protobuf:"bytes,4,rep,name=content,proto3" json:"content,omitempty"`
-	ExecutedAt           *TimeTicket `protobuf:"bytes,5,opt,name=executed_at,json=executedAt,proto3" json:"executed_at,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}    `json:"-"`
-	XXX_unrecognized     []byte      `json:"-"`
-	XXX_sizecache        int32       `json:"-"`
+	ParentCreatedAt      *TimeTicket  `protobuf:"bytes,1,opt,name=parent_created_at,json=parentCreatedAt,proto3" json:"parent_created_at,omitempty"`
+	From                 *TreePos     `protobuf:"bytes,2,opt,name=from,proto3" json:"from,omitempty"`
+	To                   *TreePos     `protobuf:"bytes,3,opt,name=to,proto3" json:"to,omitempty"`
+	Contents             []*TreeNodes `protobuf:"bytes,4,rep,name=contents,proto3" json:"contents,omitempty"`
+	ExecutedAt           *TimeTicket  `protobuf:"bytes,5,opt,name=executed_at,json=executedAt,proto3" json:"executed_at,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}     `json:"-"`
+	XXX_unrecognized     []byte       `json:"-"`
+	XXX_sizecache        int32        `json:"-"`
 }
 
 func (m *Operation_TreeEdit) Reset()         { *m = Operation_TreeEdit{} }
@@ -1180,9 +1180,9 @@ func (m *Operation_TreeEdit) GetTo() *TreePos {
 	return nil
 }
 
-func (m *Operation_TreeEdit) GetContent() []*TreeNode {
+func (m *Operation_TreeEdit) GetContents() []*TreeNodes {
 	if m != nil {
-		return m.Content
+		return m.Contents
 	}
 	return nil
 }
@@ -2329,6 +2329,53 @@ func (m *TreeNode) GetAttributes() map[string]*NodeAttr {
 	return nil
 }
 
+type TreeNodes struct {
+	Content              []*TreeNode `protobuf:"bytes,1,rep,name=content,proto3" json:"content,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}    `json:"-"`
+	XXX_unrecognized     []byte      `json:"-"`
+	XXX_sizecache        int32       `json:"-"`
+}
+
+func (m *TreeNodes) Reset()         { *m = TreeNodes{} }
+func (m *TreeNodes) String() string { return proto.CompactTextString(m) }
+func (*TreeNodes) ProtoMessage()    {}
+func (*TreeNodes) Descriptor() ([]byte, []int) {
+	return fileDescriptor_36361b2f5d0f0896, []int{12}
+}
+func (m *TreeNodes) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *TreeNodes) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_TreeNodes.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *TreeNodes) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_TreeNodes.Merge(m, src)
+}
+func (m *TreeNodes) XXX_Size() int {
+	return m.Size()
+}
+func (m *TreeNodes) XXX_DiscardUnknown() {
+	xxx_messageInfo_TreeNodes.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_TreeNodes proto.InternalMessageInfo
+
+func (m *TreeNodes) GetContent() []*TreeNode {
+	if m != nil {
+		return m.Content
+	}
+	return nil
+}
+
 type TreePos struct {
 	CreatedAt            *TimeTicket `protobuf:"bytes,1,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
 	Offset               int32       `protobuf:"varint,2,opt,name=offset,proto3" json:"offset,omitempty"`
@@ -2341,7 +2388,7 @@ func (m *TreePos) Reset()         { *m = TreePos{} }
 func (m *TreePos) String() string { return proto.CompactTextString(m) }
 func (*TreePos) ProtoMessage()    {}
 func (*TreePos) Descriptor() ([]byte, []int) {
-	return fileDescriptor_36361b2f5d0f0896, []int{12}
+	return fileDescriptor_36361b2f5d0f0896, []int{13}
 }
 func (m *TreePos) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -2397,7 +2444,7 @@ func (m *User) Reset()         { *m = User{} }
 func (m *User) String() string { return proto.CompactTextString(m) }
 func (*User) ProtoMessage()    {}
 func (*User) Descriptor() ([]byte, []int) {
-	return fileDescriptor_36361b2f5d0f0896, []int{13}
+	return fileDescriptor_36361b2f5d0f0896, []int{14}
 }
 func (m *User) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -2466,7 +2513,7 @@ func (m *Project) Reset()         { *m = Project{} }
 func (m *Project) String() string { return proto.CompactTextString(m) }
 func (*Project) ProtoMessage()    {}
 func (*Project) Descriptor() ([]byte, []int) {
-	return fileDescriptor_36361b2f5d0f0896, []int{14}
+	return fileDescriptor_36361b2f5d0f0896, []int{15}
 }
 func (m *Project) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -2572,7 +2619,7 @@ func (m *UpdatableProjectFields) Reset()         { *m = UpdatableProjectFields{}
 func (m *UpdatableProjectFields) String() string { return proto.CompactTextString(m) }
 func (*UpdatableProjectFields) ProtoMessage()    {}
 func (*UpdatableProjectFields) Descriptor() ([]byte, []int) {
-	return fileDescriptor_36361b2f5d0f0896, []int{15}
+	return fileDescriptor_36361b2f5d0f0896, []int{16}
 }
 func (m *UpdatableProjectFields) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -2644,7 +2691,7 @@ func (m *UpdatableProjectFields_AuthWebhookMethods) String() string {
 }
 func (*UpdatableProjectFields_AuthWebhookMethods) ProtoMessage() {}
 func (*UpdatableProjectFields_AuthWebhookMethods) Descriptor() ([]byte, []int) {
-	return fileDescriptor_36361b2f5d0f0896, []int{15, 0}
+	return fileDescriptor_36361b2f5d0f0896, []int{16, 0}
 }
 func (m *UpdatableProjectFields_AuthWebhookMethods) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -2696,7 +2743,7 @@ func (m *DocumentSummary) Reset()         { *m = DocumentSummary{} }
 func (m *DocumentSummary) String() string { return proto.CompactTextString(m) }
 func (*DocumentSummary) ProtoMessage()    {}
 func (*DocumentSummary) Descriptor() ([]byte, []int) {
-	return fileDescriptor_36361b2f5d0f0896, []int{16}
+	return fileDescriptor_36361b2f5d0f0896, []int{17}
 }
 func (m *DocumentSummary) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -2779,7 +2826,7 @@ func (m *Presence) Reset()         { *m = Presence{} }
 func (m *Presence) String() string { return proto.CompactTextString(m) }
 func (*Presence) ProtoMessage()    {}
 func (*Presence) Descriptor() ([]byte, []int) {
-	return fileDescriptor_36361b2f5d0f0896, []int{17}
+	return fileDescriptor_36361b2f5d0f0896, []int{18}
 }
 func (m *Presence) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -2834,7 +2881,7 @@ func (m *Client) Reset()         { *m = Client{} }
 func (m *Client) String() string { return proto.CompactTextString(m) }
 func (*Client) ProtoMessage()    {}
 func (*Client) Descriptor() ([]byte, []int) {
-	return fileDescriptor_36361b2f5d0f0896, []int{18}
+	return fileDescriptor_36361b2f5d0f0896, []int{19}
 }
 func (m *Client) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -2889,7 +2936,7 @@ func (m *Checkpoint) Reset()         { *m = Checkpoint{} }
 func (m *Checkpoint) String() string { return proto.CompactTextString(m) }
 func (*Checkpoint) ProtoMessage()    {}
 func (*Checkpoint) Descriptor() ([]byte, []int) {
-	return fileDescriptor_36361b2f5d0f0896, []int{19}
+	return fileDescriptor_36361b2f5d0f0896, []int{20}
 }
 func (m *Checkpoint) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -2945,7 +2992,7 @@ func (m *TextNodePos) Reset()         { *m = TextNodePos{} }
 func (m *TextNodePos) String() string { return proto.CompactTextString(m) }
 func (*TextNodePos) ProtoMessage()    {}
 func (*TextNodePos) Descriptor() ([]byte, []int) {
-	return fileDescriptor_36361b2f5d0f0896, []int{20}
+	return fileDescriptor_36361b2f5d0f0896, []int{21}
 }
 func (m *TextNodePos) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -3008,7 +3055,7 @@ func (m *TimeTicket) Reset()         { *m = TimeTicket{} }
 func (m *TimeTicket) String() string { return proto.CompactTextString(m) }
 func (*TimeTicket) ProtoMessage()    {}
 func (*TimeTicket) Descriptor() ([]byte, []int) {
-	return fileDescriptor_36361b2f5d0f0896, []int{21}
+	return fileDescriptor_36361b2f5d0f0896, []int{22}
 }
 func (m *TimeTicket) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -3071,7 +3118,7 @@ func (m *DocEvent) Reset()         { *m = DocEvent{} }
 func (m *DocEvent) String() string { return proto.CompactTextString(m) }
 func (*DocEvent) ProtoMessage()    {}
 func (*DocEvent) Descriptor() ([]byte, []int) {
-	return fileDescriptor_36361b2f5d0f0896, []int{22}
+	return fileDescriptor_36361b2f5d0f0896, []int{23}
 }
 func (m *DocEvent) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -3158,6 +3205,7 @@ func init() {
 	proto.RegisterType((*TextNodeID)(nil), "yorkie.v1.TextNodeID")
 	proto.RegisterType((*TreeNode)(nil), "yorkie.v1.TreeNode")
 	proto.RegisterMapType((map[string]*NodeAttr)(nil), "yorkie.v1.TreeNode.AttributesEntry")
+	proto.RegisterType((*TreeNodes)(nil), "yorkie.v1.TreeNodes")
 	proto.RegisterType((*TreePos)(nil), "yorkie.v1.TreePos")
 	proto.RegisterType((*User)(nil), "yorkie.v1.User")
 	proto.RegisterType((*Project)(nil), "yorkie.v1.Project")
@@ -3176,159 +3224,161 @@ func init() {
 func init() { proto.RegisterFile("yorkie/v1/resources.proto", fileDescriptor_36361b2f5d0f0896) }
 
 var fileDescriptor_36361b2f5d0f0896 = []byte{
-	// 2432 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x5a, 0xcd, 0x8f, 0x1b, 0x49,
-	0x15, 0x9f, 0xb6, 0xdb, 0x1f, 0xfd, 0x3c, 0xc9, 0x38, 0x35, 0x49, 0xd6, 0xf1, 0x6e, 0x26, 0x13,
-	0x67, 0x09, 0x93, 0x04, 0x3c, 0xc9, 0x90, 0xf0, 0xb1, 0x61, 0x01, 0x8f, 0xdd, 0x9b, 0x99, 0xec,
-	0xc4, 0x33, 0x6a, 0x7b, 0x12, 0xb2, 0x02, 0xb5, 0x7a, 0xba, 0x2b, 0x71, 0xef, 0xd8, 0xdd, 0xbd,
-	0xdd, 0x65, 0x6f, 0x7c, 0x40, 0x42, 0x08, 0x24, 0x90, 0xb8, 0x70, 0xe3, 0x3f, 0x40, 0xfc, 0x09,
-	0x7b, 0xe5, 0x80, 0xe0, 0x80, 0x40, 0x62, 0x25, 0xae, 0x10, 0x0e, 0x88, 0x23, 0x42, 0x70, 0x43,
-	0x42, 0x55, 0xd5, 0xdd, 0x6e, 0xb7, 0xdb, 0x8e, 0x63, 0x46, 0xab, 0x44, 0xdc, 0x5c, 0x55, 0xbf,
-	0x57, 0xf5, 0xbe, 0xeb, 0xbd, 0x2e, 0xc3, 0x85, 0xa1, 0xed, 0x1e, 0x9b, 0x78, 0x73, 0x70, 0x6b,
-	0xd3, 0xc5, 0x9e, 0xdd, 0x77, 0x75, 0xec, 0x55, 0x1d, 0xd7, 0x26, 0x36, 0x92, 0xf8, 0x52, 0x75,
-	0x70, 0xab, 0x7c, 0xe9, 0xa9, 0x6d, 0x3f, 0xed, 0xe2, 0x4d, 0xb6, 0x70, 0xd4, 0x7f, 0xb2, 0x49,
-	0xcc, 0x1e, 0xf6, 0x88, 0xd6, 0x73, 0x38, 0xb6, 0xbc, 0x16, 0x07, 0x7c, 0xec, 0x6a, 0x8e, 0x83,
-	0x5d, 0x7f, 0xaf, 0xca, 0xcf, 0x52, 0x00, 0xf5, 0x8e, 0x66, 0x3d, 0xc5, 0x07, 0x9a, 0x7e, 0x8c,
-	0x2e, 0xc3, 0xb2, 0x61, 0xeb, 0xfd, 0x1e, 0xb6, 0x88, 0x7a, 0x8c, 0x87, 0x25, 0x61, 0x5d, 0xd8,
-	0x90, 0x94, 0x42, 0x30, 0xf7, 0x3e, 0x1e, 0xa2, 0x3b, 0x00, 0x7a, 0x07, 0xeb, 0xc7, 0x8e, 0x6d,
-	0x5a, 0xa4, 0x94, 0x5a, 0x17, 0x36, 0x0a, 0x5b, 0xe7, 0xaa, 0x21, 0x4b, 0xd5, 0x7a, 0xb8, 0xa8,
-	0x44, 0x80, 0xa8, 0x0c, 0x79, 0xcf, 0xd2, 0x1c, 0xaf, 0x63, 0x93, 0x52, 0x7a, 0x5d, 0xd8, 0x58,
-	0x56, 0xc2, 0x31, 0xba, 0x01, 0x39, 0x9d, 0xf1, 0xe0, 0x95, 0xc4, 0xf5, 0xf4, 0x46, 0x61, 0xeb,
-	0xcc, 0xd8, 0x7e, 0x74, 0x45, 0x09, 0x10, 0xa8, 0x06, 0x67, 0x7a, 0xa6, 0xa5, 0x7a, 0x43, 0x4b,
-	0xc7, 0x86, 0x4a, 0x4c, 0xfd, 0x18, 0x93, 0x52, 0x66, 0x82, 0x8d, 0xb6, 0xd9, 0xc3, 0x6d, 0xb6,
-	0xa8, 0xac, 0xf4, 0x4c, 0xab, 0xc5, 0xe0, 0x7c, 0x02, 0x5d, 0x04, 0x30, 0x3d, 0xd5, 0xc5, 0x3d,
-	0x7b, 0x80, 0x8d, 0x52, 0x76, 0x5d, 0xd8, 0xc8, 0x2b, 0x92, 0xe9, 0x29, 0x7c, 0xa2, 0xf2, 0x3d,
-	0xc8, 0xf2, 0x43, 0xd1, 0x15, 0x48, 0x99, 0x06, 0x53, 0x42, 0x61, 0x6b, 0x75, 0x82, 0xa7, 0xdd,
-	0x86, 0x92, 0x32, 0x0d, 0x54, 0x82, 0x5c, 0x0f, 0x7b, 0x9e, 0xf6, 0x14, 0x33, 0x6d, 0x48, 0x4a,
-	0x30, 0x44, 0xb7, 0x01, 0x6c, 0x07, 0xbb, 0x1a, 0x31, 0x6d, 0xcb, 0x2b, 0xa5, 0x99, 0x68, 0x67,
-	0x23, 0xdb, 0xec, 0x07, 0x8b, 0x4a, 0x04, 0x57, 0xf9, 0x91, 0x00, 0xf9, 0xe0, 0x00, 0xca, 0xaa,
-	0xde, 0x35, 0xa9, 0x39, 0x3c, 0xfc, 0x11, 0xe3, 0xe4, 0x94, 0x22, 0xf1, 0x99, 0x16, 0xfe, 0x08,
-	0x5d, 0x06, 0xf0, 0xb0, 0x3b, 0xc0, 0x2e, 0x5b, 0xa6, 0xc7, 0xa7, 0xb7, 0x53, 0x37, 0x05, 0x45,
-	0xe2, 0xb3, 0x14, 0xf2, 0x16, 0xe4, 0xba, 0x5a, 0xcf, 0xb1, 0x5d, 0xae, 0x77, 0xbe, 0x1e, 0x4c,
-	0xa1, 0x0b, 0x90, 0xd7, 0x74, 0x62, 0xbb, 0xaa, 0x69, 0x94, 0x44, 0x66, 0x96, 0x1c, 0x1b, 0xef,
-	0x1a, 0x95, 0xdf, 0x96, 0x41, 0x0a, 0x39, 0x44, 0x5f, 0x80, 0xb4, 0x87, 0x89, 0xaf, 0x8b, 0x52,
-	0x92, 0x10, 0xd5, 0x16, 0x26, 0x3b, 0x4b, 0x0a, 0x85, 0x51, 0xb4, 0x66, 0x18, 0xbe, 0x77, 0x24,
-	0xa3, 0x6b, 0x86, 0x41, 0xd1, 0x9a, 0x61, 0xa0, 0x4d, 0x10, 0xa9, 0xe6, 0x19, 0x7f, 0x85, 0xad,
-	0x0b, 0x89, 0xf0, 0x07, 0xf6, 0x00, 0xef, 0x2c, 0x29, 0x0c, 0x88, 0xee, 0x40, 0x96, 0x5b, 0x8f,
-	0xf1, 0x5c, 0xd8, 0x7a, 0x33, 0x91, 0x84, 0xdb, 0x73, 0x67, 0x49, 0xf1, 0xc1, 0xf4, 0x1c, 0x6c,
-	0x98, 0x81, 0xb7, 0x24, 0x9f, 0x23, 0x1b, 0x26, 0x95, 0x82, 0x01, 0xe9, 0x39, 0x1e, 0xee, 0x62,
-	0x9d, 0x30, 0x27, 0x99, 0x76, 0x4e, 0x8b, 0x41, 0xe8, 0x39, 0x1c, 0x8c, 0xb6, 0x20, 0xe3, 0x91,
-	0x61, 0x17, 0x97, 0x72, 0x8c, 0xaa, 0x9c, 0x4c, 0x45, 0x11, 0x3b, 0x4b, 0x0a, 0x87, 0xa2, 0xbb,
-	0x90, 0x37, 0x2d, 0xdd, 0xc5, 0x9a, 0x87, 0x4b, 0x79, 0x46, 0x76, 0x31, 0x91, 0x6c, 0xd7, 0x07,
-	0xed, 0x2c, 0x29, 0x21, 0x01, 0xfa, 0x3a, 0x48, 0xc4, 0xc5, 0x58, 0x65, 0xd2, 0x49, 0x33, 0xa8,
-	0xdb, 0x2e, 0xc6, 0xbe, 0x84, 0x79, 0xe2, 0xff, 0x46, 0xdf, 0x04, 0x60, 0xd4, 0x9c, 0x67, 0x60,
-	0xe4, 0x6b, 0x53, 0xc9, 0x03, 0xbe, 0xd9, 0x89, 0x6c, 0x50, 0xfe, 0xb5, 0x00, 0xe9, 0x16, 0x26,
-	0x34, 0x34, 0x1d, 0xcd, 0xa5, 0xce, 0x4a, 0xf9, 0x22, 0xd8, 0x50, 0xb5, 0xc0, 0x63, 0xa6, 0x85,
-	0x26, 0xc7, 0xd7, 0x39, 0xbc, 0x46, 0x50, 0x11, 0xd2, 0x34, 0xef, 0xf0, 0x40, 0xa2, 0x3f, 0xa9,
-	0x32, 0x07, 0x5a, 0xb7, 0x1f, 0x78, 0xc7, 0x5b, 0x91, 0x8d, 0xee, 0xb7, 0xf6, 0x9b, 0x72, 0x17,
-	0xd3, 0xcc, 0xd4, 0x32, 0x7b, 0x4e, 0x17, 0x2b, 0x1c, 0x8a, 0xbe, 0x0c, 0x05, 0xfc, 0x0c, 0xeb,
-	0x7d, 0x9f, 0x05, 0x71, 0x16, 0x0b, 0x10, 0x20, 0x6b, 0xa4, 0xfc, 0x4f, 0x01, 0xd2, 0x35, 0xc3,
-	0x38, 0x09, 0x41, 0xde, 0x85, 0x15, 0xc7, 0xc5, 0x83, 0xe8, 0x06, 0xa9, 0x59, 0x1b, 0x9c, 0xa2,
-	0xe8, 0x11, 0xf9, 0x67, 0x29, 0xf5, 0xbf, 0x05, 0x10, 0x69, 0x78, 0xbd, 0x02, 0x62, 0xdf, 0x06,
-	0x88, 0x50, 0xa6, 0x67, 0x51, 0x4a, 0x7a, 0x48, 0xb5, 0xa8, 0xe0, 0x9f, 0x08, 0x90, 0xe5, 0x49,
-	0xe2, 0x24, 0x44, 0x1f, 0xe7, 0x3d, 0xb5, 0x18, 0xef, 0xe9, 0x79, 0x79, 0xff, 0x95, 0x08, 0x22,
-	0x8b, 0xde, 0x13, 0xe0, 0xfc, 0x3a, 0x88, 0x4f, 0x5c, 0xbb, 0xe7, 0xf3, 0x7c, 0x3e, 0x4a, 0x85,
-	0x9f, 0x91, 0xa6, 0x6d, 0xe0, 0x03, 0xdb, 0x53, 0x18, 0x06, 0x5d, 0x85, 0x14, 0xb1, 0x7d, 0x36,
-	0xa7, 0x21, 0x53, 0xc4, 0x46, 0x1d, 0x78, 0x63, 0xc4, 0x8f, 0xda, 0xd3, 0x1c, 0xf5, 0x68, 0xa8,
-	0xb2, 0xab, 0xc5, 0xbf, 0xe3, 0xb7, 0xa6, 0xa6, 0xdf, 0x6a, 0xc8, 0xd9, 0x03, 0xcd, 0xd9, 0x1e,
-	0xd6, 0x28, 0x91, 0x6c, 0x11, 0x77, 0xa8, 0xac, 0xea, 0x93, 0x2b, 0xf4, 0xfe, 0xd5, 0x6d, 0x8b,
-	0x60, 0x8b, 0x27, 0x76, 0x49, 0x09, 0x86, 0x71, 0xdd, 0x66, 0xe7, 0xd4, 0x2d, 0xda, 0x05, 0xd0,
-	0x08, 0x71, 0xcd, 0xa3, 0x3e, 0xc1, 0x5e, 0x29, 0xc7, 0xd8, 0xbd, 0x36, 0x9d, 0xdd, 0x5a, 0x88,
-	0xe5, 0x5c, 0x46, 0x88, 0xcb, 0xdf, 0x85, 0xd2, 0x34, 0x69, 0x82, 0x5c, 0x27, 0x8c, 0x72, 0xdd,
-	0x8d, 0x20, 0xea, 0x67, 0x7a, 0x0f, 0xc7, 0xbc, 0x93, 0xfa, 0xaa, 0x50, 0x7e, 0x17, 0x56, 0x62,
-	0xa7, 0x27, 0xec, 0x7a, 0x36, 0xba, 0xab, 0x14, 0x25, 0xff, 0x93, 0x00, 0x59, 0x7e, 0x7b, 0xbd,
-	0xaa, 0x6e, 0xb4, 0x68, 0x68, 0xff, 0x25, 0x05, 0x19, 0x76, 0x39, 0xbd, 0xaa, 0x82, 0xdd, 0x1f,
-	0xf3, 0x31, 0x1e, 0x12, 0xd7, 0xa7, 0x17, 0x0a, 0xb3, 0x9c, 0x2c, 0xae, 0xa4, 0xcc, 0xbc, 0x4a,
-	0xfa, 0x1f, 0xbd, 0xe7, 0x13, 0x01, 0xf2, 0x41, 0x39, 0x72, 0x12, 0x6a, 0xde, 0x1a, 0xf7, 0xfe,
-	0x45, 0xee, 0xbc, 0xb9, 0xd3, 0xe7, 0xf7, 0x53, 0x90, 0x0f, 0x8a, 0xa1, 0x93, 0xe0, 0xfd, 0xea,
-	0x98, 0x8b, 0xa0, 0x28, 0x95, 0x8b, 0x23, 0xee, 0x51, 0x89, 0xb8, 0x47, 0x12, 0x8a, 0xba, 0xc6,
-	0x17, 0x47, 0x09, 0x8d, 0xfb, 0xc5, 0x6a, 0x0c, 0x48, 0xfd, 0x68, 0x6a, 0x96, 0xcb, 0xbc, 0x44,
-	0x88, 0x48, 0x61, 0x41, 0xf7, 0xaa, 0xe9, 0xa0, 0x99, 0x10, 0x1e, 0xd5, 0xd9, 0x35, 0xe9, 0x2b,
-	0x18, 0x22, 0xdb, 0x59, 0x10, 0x8f, 0x6c, 0x63, 0x58, 0xf9, 0x87, 0x00, 0x67, 0x26, 0x7c, 0x38,
-	0x56, 0x31, 0x08, 0x73, 0x56, 0x0c, 0x37, 0x21, 0xcf, 0xfa, 0xd4, 0x17, 0x56, 0x19, 0x39, 0x06,
-	0xe3, 0x95, 0x89, 0xdf, 0xec, 0xbe, 0xb8, 0xaa, 0xf2, 0x81, 0x35, 0x82, 0x36, 0x40, 0x24, 0x43,
-	0x87, 0xb7, 0x58, 0xa7, 0xc7, 0xfa, 0xd6, 0x87, 0x54, 0xbe, 0xf6, 0xd0, 0xc1, 0x0a, 0x43, 0x8c,
-	0xe4, 0xcf, 0xb0, 0x0e, 0x92, 0x0f, 0x2a, 0xbf, 0x3c, 0x05, 0x85, 0x88, 0xcc, 0xa8, 0x01, 0x85,
-	0x0f, 0x3d, 0xdb, 0x52, 0xed, 0xa3, 0x0f, 0x69, 0x47, 0xc5, 0xc5, 0xbd, 0x9c, 0x1c, 0xe4, 0xec,
-	0xf7, 0x3e, 0x03, 0xee, 0x2c, 0x29, 0x40, 0xe9, 0xf8, 0x08, 0xd5, 0x80, 0x8d, 0x54, 0xcd, 0x75,
-	0xb5, 0xa1, 0x2f, 0xff, 0xfa, 0x8c, 0x4d, 0x6a, 0x14, 0x47, 0xdb, 0x15, 0x4a, 0xc5, 0x06, 0xe8,
-	0x5b, 0x20, 0x39, 0xae, 0xd9, 0x33, 0x89, 0x19, 0xf6, 0x9c, 0xd3, 0x76, 0x38, 0x08, 0x70, 0x74,
-	0x87, 0x90, 0x08, 0xdd, 0x02, 0x91, 0xe0, 0x67, 0x81, 0x1b, 0xbd, 0x39, 0x85, 0x98, 0xa6, 0x7c,
-	0xda, 0x4a, 0x52, 0x28, 0x7a, 0x87, 0x06, 0x75, 0xdf, 0x22, 0xd8, 0xf5, 0xeb, 0x90, 0xb5, 0x29,
-	0x54, 0x75, 0x8e, 0xda, 0x59, 0x52, 0x02, 0x02, 0x76, 0x9c, 0x8b, 0x83, 0x76, 0x72, 0xea, 0x71,
-	0x2e, 0x66, 0x1d, 0x32, 0x85, 0x96, 0x3f, 0x15, 0x00, 0x46, 0x3a, 0x44, 0x1b, 0x90, 0xb1, 0x6c,
-	0x03, 0x7b, 0x25, 0x81, 0x45, 0x52, 0x34, 0xea, 0x94, 0x9d, 0x36, 0xcb, 0x27, 0x1c, 0xb0, 0x60,
-	0x15, 0x1b, 0xf5, 0xc9, 0xf4, 0x02, 0x3e, 0x29, 0xce, 0xe7, 0x93, 0xe5, 0x3f, 0x0a, 0x20, 0x85,
-	0x56, 0x9d, 0x29, 0xd5, 0xbd, 0xda, 0xeb, 0x23, 0xd5, 0xdf, 0x05, 0x90, 0x42, 0x4f, 0x0b, 0xe3,
-	0x4e, 0x98, 0x3f, 0xee, 0x52, 0x91, 0xb8, 0x5b, 0xb0, 0x87, 0x8a, 0xca, 0x2a, 0x2e, 0x20, 0x6b,
-	0x66, 0x4e, 0x59, 0x7f, 0x2f, 0x80, 0x48, 0x03, 0x03, 0x5d, 0x1b, 0x37, 0xde, 0x6a, 0x42, 0xad,
-	0xf4, 0x7a, 0x58, 0xef, 0x6f, 0x02, 0xe4, 0xfc, 0xa0, 0xfd, 0x7f, 0xb0, 0x9d, 0x8b, 0xf1, 0x4c,
-	0xdb, 0x05, 0xf5, 0xc9, 0xeb, 0x60, 0xbb, 0xf0, 0x7e, 0x7e, 0x00, 0x39, 0x3f, 0x0f, 0x26, 0x5c,
-	0xef, 0x37, 0x21, 0x87, 0x79, 0x8e, 0x4d, 0xe8, 0x00, 0x22, 0x19, 0x58, 0x09, 0x60, 0x15, 0x1d,
-	0x72, 0x7e, 0x02, 0xa2, 0x45, 0x91, 0x45, 0xaf, 0x0a, 0x61, 0xa2, 0xdc, 0x09, 0x52, 0x14, 0x5b,
-	0x5f, 0xe0, 0x90, 0x87, 0x90, 0xa7, 0xf4, 0xb4, 0x3c, 0x19, 0x79, 0x93, 0x10, 0xa9, 0x40, 0xa8,
-	0x4e, 0xfa, 0x8e, 0x31, 0x9f, 0xee, 0x7d, 0x60, 0x8d, 0x54, 0x7e, 0x47, 0x4b, 0x63, 0x3f, 0x02,
-	0xd1, 0xe7, 0x22, 0x5f, 0xc0, 0xcf, 0x25, 0x84, 0xa8, 0xff, 0x0d, 0x3c, 0xb1, 0x02, 0x5a, 0xb0,
-	0xee, 0xb8, 0x03, 0x05, 0xd3, 0xf2, 0x54, 0xf6, 0x19, 0xc9, 0xff, 0x2a, 0x3d, 0xf5, 0x6c, 0xc9,
-	0xb4, 0xbc, 0x03, 0x17, 0x0f, 0x76, 0x0d, 0x54, 0x1f, 0xab, 0x18, 0x33, 0xcc, 0x31, 0xaf, 0x24,
-	0x50, 0xcd, 0x6c, 0xd7, 0x95, 0x79, 0xca, 0xbd, 0x6b, 0xe3, 0x7d, 0x4a, 0xd4, 0xfb, 0x03, 0x83,
-	0x44, 0x6a, 0xc0, 0xca, 0x07, 0x00, 0x23, 0x8e, 0x17, 0xac, 0xf9, 0xce, 0x43, 0xd6, 0x7e, 0xf2,
-	0xc4, 0xc3, 0xdc, 0x8a, 0x19, 0xc5, 0x1f, 0x55, 0xfe, 0xe5, 0xb7, 0x31, 0xcc, 0x56, 0x6f, 0x43,
-	0xda, 0xb1, 0xbd, 0x04, 0x4f, 0x0b, 0x0a, 0x6b, 0xba, 0x8c, 0x90, 0x9f, 0xa2, 0xb8, 0xa5, 0x62,
-	0xc9, 0x28, 0x3d, 0xdd, 0x7c, 0x73, 0x86, 0x14, 0xba, 0x0d, 0xcb, 0xa1, 0xf9, 0x28, 0x3b, 0x99,
-	0xa9, 0xec, 0x80, 0x6f, 0xbc, 0x03, 0xdb, 0xa3, 0x1c, 0x18, 0xd8, 0x21, 0x1d, 0x56, 0x1c, 0x65,
-	0x14, 0x3e, 0x88, 0xd9, 0x34, 0x37, 0x69, 0x53, 0x5f, 0xf4, 0xcf, 0xdc, 0xa6, 0x8f, 0x20, 0xe7,
-	0x4b, 0x71, 0xc2, 0x06, 0xed, 0x81, 0x78, 0xe8, 0x61, 0x17, 0x9d, 0x0e, 0xe3, 0x4e, 0x62, 0x01,
-	0x56, 0x86, 0x7c, 0xdf, 0xc3, 0xae, 0xa5, 0xf5, 0x02, 0xcb, 0x85, 0x63, 0xf4, 0xb5, 0x84, 0x4b,
-	0xa3, 0x5c, 0xe5, 0x0f, 0x7f, 0xd5, 0xe0, 0xe1, 0x8f, 0xf1, 0xc1, 0x5e, 0x06, 0x23, 0x6c, 0x54,
-	0xfe, 0x93, 0x82, 0xdc, 0x81, 0x6b, 0xb3, 0x1a, 0x31, 0x7e, 0x24, 0x02, 0x31, 0x72, 0x1c, 0xfb,
-	0x8d, 0x2e, 0x02, 0x38, 0xfd, 0xa3, 0xae, 0xa9, 0xb3, 0xd7, 0x41, 0xee, 0x2d, 0x12, 0x9f, 0x79,
-	0x1f, 0x0f, 0xe9, 0xb2, 0x87, 0x75, 0x17, 0xf3, 0xc7, 0x43, 0x91, 0x2f, 0xf3, 0x19, 0xba, 0xbc,
-	0x01, 0x45, 0xad, 0x4f, 0x3a, 0xea, 0xc7, 0xf8, 0xa8, 0x63, 0xdb, 0xc7, 0x6a, 0xdf, 0xed, 0xfa,
-	0x9f, 0xec, 0x4e, 0xd3, 0xf9, 0x47, 0x7c, 0xfa, 0xd0, 0xed, 0xa2, 0x9b, 0x70, 0x76, 0x0c, 0xd9,
-	0xc3, 0xa4, 0x63, 0x1b, 0x5e, 0x29, 0xbb, 0x9e, 0xde, 0x90, 0x14, 0x14, 0x41, 0x3f, 0xe0, 0x2b,
-	0xe8, 0x1b, 0xf0, 0xa6, 0xff, 0x50, 0x66, 0x60, 0x4d, 0x27, 0xe6, 0x40, 0x23, 0x58, 0x25, 0x1d,
-	0x17, 0x7b, 0x1d, 0xbb, 0x6b, 0xb0, 0xd2, 0x59, 0x52, 0x2e, 0x70, 0x48, 0x23, 0x44, 0xb4, 0x03,
-	0x40, 0x4c, 0x89, 0xf9, 0x97, 0x50, 0x22, 0x25, 0x8d, 0xa4, 0x59, 0xe9, 0xc5, 0xa4, 0xa3, 0x5c,
-	0xfb, 0xe3, 0x34, 0x9c, 0x3f, 0xa4, 0x23, 0xed, 0xa8, 0x8b, 0x7d, 0x43, 0xbc, 0x67, 0xe2, 0xae,
-	0xe1, 0xa1, 0x9b, 0xbe, 0xfa, 0x05, 0xff, 0x63, 0x48, 0x7c, 0xbf, 0x16, 0x71, 0x4d, 0xeb, 0x29,
-	0x2b, 0x2b, 0x7c, 0xe3, 0xbc, 0x97, 0xa0, 0xde, 0xd4, 0x1c, 0xd4, 0x71, 0xe5, 0x3f, 0x99, 0xa2,
-	0x7c, 0xee, 0x59, 0xb7, 0x23, 0xbe, 0x9d, 0xcc, 0x7a, 0xb5, 0x36, 0x61, 0x9e, 0x44, 0x93, 0x7d,
-	0x67, 0xb6, 0xc9, 0xc4, 0x39, 0x58, 0x9f, 0x6e, 0xd0, 0x72, 0x15, 0xd0, 0x24, 0x1f, 0xfc, 0xb1,
-	0x96, 0x8b, 0x23, 0x30, 0x5f, 0x0a, 0x86, 0x95, 0x1f, 0xa4, 0x60, 0xa5, 0xe1, 0xbf, 0x73, 0xb7,
-	0xfa, 0xbd, 0x9e, 0xe6, 0x0e, 0x27, 0x42, 0x62, 0xf2, 0x75, 0x2a, 0xfe, 0xac, 0x2d, 0x45, 0x9e,
-	0xb5, 0xc7, 0x5d, 0x4a, 0x7c, 0x19, 0x97, 0xba, 0x0b, 0x05, 0x4d, 0xd7, 0xb1, 0xe7, 0x45, 0x0b,
-	0xb4, 0x59, 0xb4, 0x10, 0xc0, 0x27, 0xfc, 0x31, 0xfb, 0x32, 0xfe, 0xf8, 0x53, 0x01, 0xf2, 0x07,
-	0x2e, 0xf6, 0xb0, 0xa5, 0xb3, 0x5b, 0x41, 0xef, 0xda, 0xfa, 0x31, 0x53, 0x40, 0x46, 0xe1, 0x03,
-	0xda, 0x8c, 0x52, 0xa3, 0x97, 0x52, 0x2c, 0x1b, 0x47, 0x9f, 0x19, 0x03, 0xc2, 0x6a, 0x43, 0x23,
-	0x1a, 0xcf, 0xc3, 0x0c, 0x5a, 0xfe, 0x0a, 0x48, 0xe1, 0xd4, 0xcb, 0x7c, 0x3e, 0xa9, 0xec, 0x42,
-	0xb6, 0xce, 0x0c, 0x1c, 0xb1, 0xc4, 0x32, 0xb3, 0xc4, 0x26, 0xe4, 0x1d, 0xff, 0xb8, 0x84, 0x94,
-	0x1d, 0x70, 0xa2, 0x84, 0xa0, 0x4a, 0x13, 0x60, 0xf4, 0xcf, 0x84, 0xd8, 0xbb, 0xb9, 0x90, 0xf4,
-	0x6e, 0x3e, 0xfe, 0xf2, 0x9e, 0x8a, 0xbd, 0xbc, 0x57, 0x7e, 0x28, 0x40, 0x21, 0xf2, 0x4d, 0xf7,
-	0x64, 0xaf, 0x01, 0xf4, 0x79, 0x58, 0x71, 0x71, 0x57, 0xa3, 0xfd, 0xa0, 0xea, 0x03, 0xd2, 0x0c,
-	0x70, 0x3a, 0x98, 0xde, 0xe7, 0xf7, 0x85, 0x0e, 0x30, 0xda, 0x39, 0xfa, 0xd6, 0x2f, 0x4c, 0xbe,
-	0xf5, 0xbf, 0x05, 0x92, 0x81, 0xbb, 0xb4, 0xcd, 0xc4, 0x6e, 0x20, 0x50, 0x38, 0x31, 0xf6, 0x4f,
-	0x80, 0xf4, 0xf8, 0x3f, 0x01, 0x7e, 0x22, 0x40, 0xbe, 0x61, 0xeb, 0xf2, 0x80, 0x5a, 0xe2, 0xc6,
-	0x58, 0x8b, 0xf3, 0x46, 0x44, 0xc4, 0x00, 0x12, 0xe9, 0x72, 0x36, 0x81, 0xdf, 0x0e, 0x5e, 0xc7,
-	0x3f, 0x32, 0xf6, 0xdf, 0x0e, 0xa6, 0x4e, 0x65, 0x84, 0x41, 0x97, 0x20, 0xfc, 0xb3, 0x49, 0xc0,
-	0x88, 0xa4, 0x40, 0x30, 0xb5, 0x6b, 0x5c, 0xff, 0x34, 0x05, 0x52, 0xd8, 0x4b, 0xa1, 0x55, 0x58,
-	0x79, 0x58, 0xdb, 0x3b, 0x94, 0xd5, 0xf6, 0xe3, 0x03, 0x59, 0x6d, 0x1e, 0xee, 0xed, 0x15, 0x97,
-	0xd0, 0x79, 0x40, 0x91, 0xc9, 0xed, 0xfd, 0xfd, 0x3d, 0xb9, 0xd6, 0x2c, 0x0a, 0xb1, 0xf9, 0xdd,
-	0x66, 0x5b, 0xbe, 0x27, 0x2b, 0xc5, 0x54, 0x6c, 0x93, 0xbd, 0xfd, 0xe6, 0xbd, 0x62, 0x1a, 0x9d,
-	0x83, 0x33, 0x91, 0xc9, 0xc6, 0xfe, 0xe1, 0xf6, 0x9e, 0x5c, 0x14, 0x63, 0xd3, 0xad, 0xb6, 0xb2,
-	0xdb, 0xbc, 0x57, 0xcc, 0xa0, 0xb3, 0x50, 0x8c, 0x1e, 0xf9, 0xb8, 0x2d, 0xb7, 0x8a, 0xd9, 0xd8,
-	0xc6, 0x8d, 0x5a, 0x5b, 0x2e, 0xe6, 0x50, 0x19, 0xce, 0x47, 0x26, 0x69, 0x65, 0xaf, 0xee, 0x6f,
-	0xdf, 0x97, 0xeb, 0xed, 0x62, 0x1e, 0x5d, 0x80, 0x73, 0xf1, 0xb5, 0x9a, 0xa2, 0xd4, 0x1e, 0x17,
-	0xa5, 0xd8, 0x5e, 0x6d, 0xf9, 0xdb, 0xed, 0x22, 0xc4, 0xf6, 0xf2, 0x25, 0x52, 0xeb, 0xcd, 0x76,
-	0xb1, 0x80, 0xde, 0x80, 0xd5, 0x98, 0x54, 0x6c, 0x61, 0x39, 0xbe, 0x93, 0x22, 0xcb, 0xc5, 0x53,
-	0xd7, 0x7f, 0x21, 0xc0, 0x72, 0xd4, 0x7e, 0xe8, 0x6d, 0x58, 0x6f, 0xec, 0xd7, 0x55, 0xf9, 0xa1,
-	0xdc, 0x6c, 0x07, 0x3a, 0xa8, 0x1f, 0x3e, 0x90, 0x9b, 0xed, 0x96, 0x5a, 0xdf, 0xa9, 0x35, 0xef,
-	0xc9, 0x8d, 0xe2, 0xd2, 0x4c, 0xd4, 0xa3, 0x5a, 0xbb, 0xbe, 0x23, 0x37, 0x8a, 0x02, 0xba, 0x0a,
-	0x95, 0xa9, 0xa8, 0xc3, 0x66, 0x80, 0x4b, 0xa1, 0x2b, 0x70, 0x29, 0x86, 0x3b, 0x50, 0xe4, 0x96,
-	0xdc, 0xac, 0xcb, 0xe1, 0x91, 0xe9, 0xed, 0x1b, 0xbf, 0x79, 0xbe, 0x26, 0xfc, 0xe1, 0xf9, 0x9a,
-	0xf0, 0xe7, 0xe7, 0x6b, 0xc2, 0xcf, 0xff, 0xba, 0xb6, 0x04, 0x67, 0x0c, 0x3c, 0x08, 0x9c, 0x4a,
-	0x73, 0xcc, 0xea, 0xe0, 0xd6, 0x81, 0xf0, 0x81, 0x58, 0xbd, 0x3b, 0xb8, 0x75, 0x94, 0x65, 0xe9,
-	0xee, 0x4b, 0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xe3, 0x01, 0xec, 0x60, 0x4e, 0x25, 0x00, 0x00,
+	// 2452 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x5a, 0x4d, 0x6f, 0xdb, 0xc8,
+	0xf9, 0x37, 0xa9, 0x57, 0x3e, 0x72, 0x62, 0x65, 0xf2, 0xa6, 0x28, 0x89, 0xe3, 0x28, 0xfb, 0xcf,
+	0xdf, 0x49, 0x5a, 0xd9, 0x71, 0x93, 0xbe, 0x24, 0xdd, 0xb6, 0xb2, 0xcc, 0x8d, 0x9d, 0x75, 0x64,
+	0x63, 0x24, 0x27, 0xcd, 0xa2, 0x05, 0x41, 0x93, 0x93, 0x88, 0x6b, 0x89, 0xe4, 0x92, 0x94, 0x36,
+	0x3a, 0xf4, 0xd2, 0x17, 0xa0, 0x05, 0x7a, 0xe9, 0xad, 0xdf, 0xa0, 0xe8, 0x47, 0xd8, 0x6b, 0x0f,
+	0x45, 0x0f, 0x2d, 0x5a, 0xa0, 0x0b, 0xf4, 0xda, 0x4d, 0x0f, 0x45, 0x8f, 0x45, 0xd1, 0xde, 0x0a,
+	0x14, 0x33, 0x43, 0x52, 0x14, 0x45, 0x29, 0x8a, 0x6a, 0x2c, 0x12, 0xf4, 0xa6, 0x99, 0xf9, 0x3d,
+	0x33, 0xcf, 0xfb, 0x3c, 0x0f, 0x47, 0x70, 0x61, 0x60, 0x39, 0x47, 0x06, 0x59, 0xeb, 0xdf, 0x5e,
+	0x73, 0x88, 0x6b, 0xf5, 0x1c, 0x8d, 0xb8, 0x55, 0xdb, 0xb1, 0x3c, 0x0b, 0x49, 0x7c, 0xa9, 0xda,
+	0xbf, 0x5d, 0xbe, 0xf2, 0xdc, 0xb2, 0x9e, 0x77, 0xc8, 0x1a, 0x5b, 0x38, 0xec, 0x3d, 0x5b, 0xf3,
+	0x8c, 0x2e, 0x71, 0x3d, 0xb5, 0x6b, 0x73, 0x6c, 0x79, 0x39, 0x0e, 0xf8, 0xd8, 0x51, 0x6d, 0x9b,
+	0x38, 0xfe, 0x5e, 0x95, 0x9f, 0x89, 0x00, 0xf5, 0xb6, 0x6a, 0x3e, 0x27, 0xfb, 0xaa, 0x76, 0x84,
+	0xae, 0xc2, 0xa2, 0x6e, 0x69, 0xbd, 0x2e, 0x31, 0x3d, 0xe5, 0x88, 0x0c, 0x4a, 0xc2, 0x8a, 0xb0,
+	0x2a, 0xe1, 0x42, 0x30, 0xf7, 0x3e, 0x19, 0xa0, 0xbb, 0x00, 0x5a, 0x9b, 0x68, 0x47, 0xb6, 0x65,
+	0x98, 0x5e, 0x49, 0x5c, 0x11, 0x56, 0x0b, 0x1b, 0x67, 0xab, 0x21, 0x4b, 0xd5, 0x7a, 0xb8, 0x88,
+	0x23, 0x40, 0x54, 0x86, 0xbc, 0x6b, 0xaa, 0xb6, 0xdb, 0xb6, 0xbc, 0x52, 0x6a, 0x45, 0x58, 0x5d,
+	0xc4, 0xe1, 0x18, 0xdd, 0x82, 0x9c, 0xc6, 0x78, 0x70, 0x4b, 0xe9, 0x95, 0xd4, 0x6a, 0x61, 0xe3,
+	0xd4, 0xc8, 0x7e, 0x74, 0x05, 0x07, 0x08, 0x54, 0x83, 0x53, 0x5d, 0xc3, 0x54, 0xdc, 0x81, 0xa9,
+	0x11, 0x5d, 0xf1, 0x0c, 0xed, 0x88, 0x78, 0xa5, 0xcc, 0x18, 0x1b, 0x2d, 0xa3, 0x4b, 0x5a, 0x6c,
+	0x11, 0x2f, 0x75, 0x0d, 0xb3, 0xc9, 0xe0, 0x7c, 0x02, 0x5d, 0x06, 0x30, 0x5c, 0xc5, 0x21, 0x5d,
+	0xab, 0x4f, 0xf4, 0x52, 0x76, 0x45, 0x58, 0xcd, 0x63, 0xc9, 0x70, 0x31, 0x9f, 0xa8, 0x7c, 0x0f,
+	0xb2, 0xfc, 0x50, 0x74, 0x0d, 0x44, 0x43, 0x67, 0x4a, 0x28, 0x6c, 0x9c, 0x1e, 0xe3, 0x69, 0x67,
+	0x0b, 0x8b, 0x86, 0x8e, 0x4a, 0x90, 0xeb, 0x12, 0xd7, 0x55, 0x9f, 0x13, 0xa6, 0x0d, 0x09, 0x07,
+	0x43, 0x74, 0x07, 0xc0, 0xb2, 0x89, 0xa3, 0x7a, 0x86, 0x65, 0xba, 0xa5, 0x14, 0x13, 0xed, 0x4c,
+	0x64, 0x9b, 0xbd, 0x60, 0x11, 0x47, 0x70, 0x95, 0x1f, 0x09, 0x90, 0x0f, 0x0e, 0xa0, 0xac, 0x6a,
+	0x1d, 0x83, 0x9a, 0xc3, 0x25, 0x1f, 0x31, 0x4e, 0x4e, 0x60, 0x89, 0xcf, 0x34, 0xc9, 0x47, 0xe8,
+	0x2a, 0x80, 0x4b, 0x9c, 0x3e, 0x71, 0xd8, 0x32, 0x3d, 0x3e, 0xb5, 0x29, 0xae, 0x0b, 0x58, 0xe2,
+	0xb3, 0x14, 0x72, 0x09, 0x72, 0x1d, 0xb5, 0x6b, 0x5b, 0x0e, 0xd7, 0x3b, 0x5f, 0x0f, 0xa6, 0xd0,
+	0x05, 0xc8, 0xab, 0x9a, 0x67, 0x39, 0x8a, 0xa1, 0x97, 0xd2, 0xcc, 0x2c, 0x39, 0x36, 0xde, 0xd1,
+	0x2b, 0xbf, 0x2d, 0x83, 0x14, 0x72, 0x88, 0xbe, 0x00, 0x29, 0x97, 0x78, 0xbe, 0x2e, 0x4a, 0x49,
+	0x42, 0x54, 0x9b, 0xc4, 0xdb, 0x5e, 0xc0, 0x14, 0x46, 0xd1, 0xaa, 0xae, 0xfb, 0xde, 0x91, 0x8c,
+	0xae, 0xe9, 0x3a, 0x45, 0xab, 0xba, 0x8e, 0xd6, 0x20, 0x4d, 0x35, 0xcf, 0xf8, 0x2b, 0x6c, 0x5c,
+	0x48, 0x84, 0x3f, 0xb2, 0xfa, 0x64, 0x7b, 0x01, 0x33, 0x20, 0xba, 0x0b, 0x59, 0x6e, 0x3d, 0xc6,
+	0x73, 0x61, 0xe3, 0x62, 0x22, 0x09, 0xb7, 0xe7, 0xf6, 0x02, 0xf6, 0xc1, 0xf4, 0x1c, 0xa2, 0x1b,
+	0x81, 0xb7, 0x24, 0x9f, 0x23, 0xeb, 0x06, 0x95, 0x82, 0x01, 0xe9, 0x39, 0x2e, 0xe9, 0x10, 0xcd,
+	0x63, 0x4e, 0x32, 0xe9, 0x9c, 0x26, 0x83, 0xd0, 0x73, 0x38, 0x18, 0x6d, 0x40, 0xc6, 0xf5, 0x06,
+	0x1d, 0x52, 0xca, 0x31, 0xaa, 0x72, 0x32, 0x15, 0x45, 0x6c, 0x2f, 0x60, 0x0e, 0x45, 0xf7, 0x21,
+	0x6f, 0x98, 0x9a, 0x43, 0x54, 0x97, 0x94, 0xf2, 0x8c, 0xec, 0x72, 0x22, 0xd9, 0x8e, 0x0f, 0xda,
+	0x5e, 0xc0, 0x21, 0x01, 0xfa, 0x3a, 0x48, 0x9e, 0x43, 0x88, 0xc2, 0xa4, 0x93, 0xa6, 0x50, 0xb7,
+	0x1c, 0x42, 0x7c, 0x09, 0xf3, 0x9e, 0xff, 0x1b, 0x7d, 0x13, 0x80, 0x51, 0x73, 0x9e, 0x81, 0x91,
+	0x2f, 0x4f, 0x24, 0x0f, 0xf8, 0x66, 0x27, 0xb2, 0x41, 0xf9, 0xd7, 0x02, 0xa4, 0x9a, 0xc4, 0xa3,
+	0xa1, 0x69, 0xab, 0x0e, 0x75, 0x56, 0xca, 0x97, 0x47, 0x74, 0x45, 0x0d, 0x3c, 0x66, 0x52, 0x68,
+	0x72, 0x7c, 0x9d, 0xc3, 0x6b, 0x1e, 0x2a, 0x42, 0x8a, 0xe6, 0x1d, 0x1e, 0x48, 0xf4, 0x27, 0x55,
+	0x66, 0x5f, 0xed, 0xf4, 0x02, 0xef, 0xb8, 0x14, 0xd9, 0xe8, 0x61, 0x73, 0xaf, 0x21, 0x77, 0x08,
+	0xcd, 0x4c, 0x4d, 0xa3, 0x6b, 0x77, 0x08, 0xe6, 0x50, 0xf4, 0x65, 0x28, 0x90, 0x17, 0x44, 0xeb,
+	0xf9, 0x2c, 0xa4, 0xa7, 0xb1, 0x00, 0x01, 0xb2, 0xe6, 0x95, 0xff, 0x21, 0x40, 0xaa, 0xa6, 0xeb,
+	0xc7, 0x21, 0xc8, 0xbb, 0xb0, 0x64, 0x3b, 0xa4, 0x1f, 0xdd, 0x40, 0x9c, 0xb6, 0xc1, 0x09, 0x8a,
+	0x1e, 0x92, 0x7f, 0x9e, 0x52, 0xff, 0x4b, 0x80, 0x34, 0x0d, 0xaf, 0x37, 0x40, 0xec, 0x3b, 0x00,
+	0x11, 0xca, 0xd4, 0x34, 0x4a, 0x49, 0x0b, 0xa9, 0xe6, 0x15, 0xfc, 0x13, 0x01, 0xb2, 0x3c, 0x49,
+	0x1c, 0x87, 0xe8, 0xa3, 0xbc, 0x8b, 0xf3, 0xf1, 0x9e, 0x9a, 0x95, 0xf7, 0x5f, 0xa5, 0x21, 0xcd,
+	0xa2, 0xf7, 0x18, 0x38, 0xbf, 0x09, 0xe9, 0x67, 0x8e, 0xd5, 0xf5, 0x79, 0x3e, 0x17, 0xa5, 0x22,
+	0x2f, 0xbc, 0x86, 0xa5, 0x93, 0x7d, 0xcb, 0xc5, 0x0c, 0x83, 0xae, 0x83, 0xe8, 0x59, 0x3e, 0x9b,
+	0x93, 0x90, 0xa2, 0x67, 0xa1, 0x36, 0x9c, 0x1f, 0xf2, 0xa3, 0x74, 0x55, 0x5b, 0x39, 0x1c, 0x28,
+	0xec, 0x6a, 0xf1, 0xef, 0xf8, 0x8d, 0x89, 0xe9, 0xb7, 0x1a, 0x72, 0xf6, 0x48, 0xb5, 0x37, 0x07,
+	0x35, 0x4a, 0x24, 0x9b, 0x9e, 0x33, 0xc0, 0xa7, 0xb5, 0xf1, 0x15, 0x7a, 0xff, 0x6a, 0x96, 0xe9,
+	0x11, 0x93, 0x27, 0x76, 0x09, 0x07, 0xc3, 0xb8, 0x6e, 0xb3, 0x33, 0xea, 0x16, 0xed, 0x00, 0xa8,
+	0x9e, 0xe7, 0x18, 0x87, 0x3d, 0x8f, 0xb8, 0xa5, 0x1c, 0x63, 0xf7, 0xc6, 0x64, 0x76, 0x6b, 0x21,
+	0x96, 0x73, 0x19, 0x21, 0x2e, 0x7f, 0x17, 0x4a, 0x93, 0xa4, 0x09, 0x72, 0x9d, 0x30, 0xcc, 0x75,
+	0xb7, 0x82, 0xa8, 0x9f, 0xea, 0x3d, 0x1c, 0x73, 0x4f, 0xfc, 0xaa, 0x50, 0x7e, 0x17, 0x96, 0x62,
+	0xa7, 0x27, 0xec, 0x7a, 0x26, 0xba, 0xab, 0x14, 0x25, 0xff, 0x93, 0x00, 0x59, 0x7e, 0x7b, 0xbd,
+	0xa9, 0x6e, 0x34, 0x6f, 0x68, 0x7f, 0x26, 0x42, 0x86, 0x5d, 0x4e, 0x6f, 0xaa, 0x60, 0x0f, 0x47,
+	0x7c, 0x8c, 0x87, 0xc4, 0xcd, 0xc9, 0x85, 0xc2, 0x34, 0x27, 0x8b, 0x2b, 0x29, 0x33, 0xab, 0x92,
+	0xfe, 0x4b, 0xef, 0xf9, 0x44, 0x80, 0x7c, 0x50, 0x8e, 0x1c, 0x87, 0x9a, 0x37, 0x46, 0xbd, 0x7f,
+	0x9e, 0x3b, 0x6f, 0xe6, 0xf4, 0xf9, 0x03, 0x11, 0xf2, 0x41, 0x31, 0x74, 0x1c, 0xbc, 0x5f, 0x1f,
+	0x71, 0x11, 0x14, 0xa5, 0x72, 0x48, 0xc4, 0x3d, 0x2a, 0x11, 0xf7, 0x48, 0x42, 0x51, 0xd7, 0x58,
+	0x87, 0xbc, 0x9f, 0xc1, 0x02, 0xc7, 0x38, 0x13, 0x43, 0x52, 0x47, 0x72, 0x71, 0x88, 0x9a, 0xdb,
+	0x01, 0x3e, 0x13, 0x41, 0x0a, 0x6b, 0xba, 0x37, 0x4d, 0x0d, 0x8d, 0x84, 0x08, 0xa9, 0x4e, 0x2f,
+	0x4b, 0xdf, 0xc0, 0x28, 0xd9, 0xcc, 0x42, 0xfa, 0xd0, 0xd2, 0x07, 0x95, 0xbf, 0x0b, 0x70, 0x6a,
+	0xcc, 0x8d, 0x63, 0x45, 0x83, 0x30, 0x63, 0xd1, 0xb0, 0x0e, 0x79, 0xd6, 0xaa, 0xbe, 0xb2, 0xd0,
+	0xc8, 0x31, 0x18, 0x2f, 0x4e, 0xfc, 0x7e, 0xf7, 0xd5, 0x85, 0x95, 0x0f, 0xac, 0x79, 0x68, 0x15,
+	0xd2, 0xde, 0xc0, 0xe6, 0x5d, 0xd6, 0xc9, 0x11, 0x2f, 0x7c, 0x4c, 0xe5, 0x6b, 0x0d, 0x6c, 0x82,
+	0x19, 0x62, 0x28, 0x7f, 0x86, 0x35, 0x91, 0x7c, 0x50, 0xf9, 0xe5, 0x09, 0x28, 0x44, 0x64, 0x46,
+	0x5b, 0x50, 0xf8, 0xd0, 0xb5, 0x4c, 0xc5, 0x3a, 0xfc, 0x90, 0x36, 0x55, 0x5c, 0xdc, 0xab, 0xc9,
+	0x71, 0xce, 0x7e, 0xef, 0x31, 0xe0, 0xf6, 0x02, 0x06, 0x4a, 0xc7, 0x47, 0xa8, 0x06, 0x6c, 0xa4,
+	0xa8, 0x8e, 0xa3, 0x0e, 0x7c, 0xf9, 0x57, 0xa6, 0x6c, 0x52, 0xa3, 0x38, 0xda, 0xb1, 0x50, 0x2a,
+	0x36, 0x40, 0xdf, 0x02, 0xc9, 0x76, 0x8c, 0xae, 0xe1, 0x19, 0x61, 0xdb, 0x39, 0x69, 0x87, 0xfd,
+	0x00, 0x47, 0x77, 0x08, 0x89, 0xd0, 0x6d, 0x48, 0x7b, 0xe4, 0x45, 0xe0, 0x46, 0x17, 0x27, 0x10,
+	0xd3, 0xac, 0x4f, 0xbb, 0x49, 0x0a, 0x45, 0xf7, 0x68, 0xa1, 0xd2, 0x33, 0x3d, 0xe2, 0xf8, 0xa5,
+	0xc8, 0xf2, 0x04, 0xaa, 0x3a, 0x47, 0x6d, 0x2f, 0xe0, 0x80, 0x80, 0x1d, 0xe7, 0x90, 0xa0, 0xa3,
+	0x9c, 0x78, 0x9c, 0x43, 0x58, 0x93, 0x4c, 0xa1, 0xe5, 0x4f, 0x05, 0x80, 0xa1, 0x0e, 0xd1, 0x2a,
+	0x64, 0x4c, 0x9a, 0x36, 0x4a, 0x02, 0x8b, 0xa4, 0x68, 0xd4, 0xe1, 0xed, 0x16, 0xcd, 0x28, 0x98,
+	0x03, 0xe6, 0x2c, 0x64, 0xa3, 0x3e, 0x99, 0x9a, 0xc3, 0x27, 0xd3, 0xb3, 0xf9, 0x64, 0xf9, 0x8f,
+	0x02, 0x48, 0xa1, 0x55, 0xa7, 0x4a, 0xf5, 0xa0, 0xf6, 0xf6, 0x48, 0xf5, 0x37, 0x01, 0xa4, 0xd0,
+	0xd3, 0xc2, 0xb8, 0x13, 0x66, 0x8f, 0x3b, 0x31, 0x12, 0x77, 0x73, 0xb6, 0x51, 0x51, 0x59, 0xd3,
+	0x73, 0xc8, 0x9a, 0x99, 0x51, 0xd6, 0xdf, 0x0b, 0x90, 0xa6, 0x81, 0x81, 0x6e, 0x8c, 0x1a, 0xef,
+	0x74, 0x42, 0xb9, 0xf4, 0x76, 0x58, 0xef, 0xaf, 0x02, 0xe4, 0xfc, 0xa0, 0xfd, 0x5f, 0xb0, 0x9d,
+	0x43, 0xc8, 0x54, 0xdb, 0xf9, 0x15, 0xca, 0x5b, 0x61, 0xbb, 0xf0, 0x7e, 0x7e, 0x04, 0x39, 0x3f,
+	0x0f, 0x26, 0x5c, 0xef, 0xeb, 0x90, 0x23, 0x3c, 0xc7, 0x26, 0x34, 0x01, 0x91, 0x0c, 0x8c, 0x03,
+	0x58, 0x45, 0x83, 0x9c, 0x9f, 0x80, 0x68, 0x51, 0x64, 0xd2, 0xab, 0x42, 0x18, 0x2b, 0x77, 0x82,
+	0x14, 0xc5, 0xd6, 0xe7, 0x38, 0xe4, 0x31, 0xe4, 0x29, 0x3d, 0x2d, 0x4f, 0x86, 0xde, 0x24, 0x44,
+	0x2a, 0x10, 0xaa, 0x93, 0x9e, 0xad, 0xcf, 0xa6, 0x7b, 0x1f, 0x58, 0xf3, 0x2a, 0xbf, 0xa3, 0xd5,
+	0xb1, 0x1f, 0x81, 0xe8, 0xff, 0x22, 0x1f, 0xc1, 0xcf, 0x26, 0x84, 0xa8, 0xff, 0x19, 0x3c, 0xb1,
+	0x02, 0x9a, 0xb3, 0xee, 0xb8, 0x0b, 0x05, 0xc3, 0x74, 0x15, 0xf6, 0x25, 0xc9, 0xff, 0x30, 0x3d,
+	0xf1, 0x6c, 0xc9, 0x30, 0xdd, 0x7d, 0x87, 0xf4, 0x77, 0x74, 0x54, 0x1f, 0xa9, 0x18, 0x33, 0xcc,
+	0x31, 0xaf, 0x25, 0x50, 0x4d, 0xed, 0xd8, 0xf1, 0x2c, 0xe5, 0xde, 0x8d, 0xd1, 0x56, 0x25, 0xea,
+	0xfd, 0x81, 0x41, 0x22, 0x35, 0x60, 0xe5, 0x03, 0x80, 0x21, 0xc7, 0x73, 0xd6, 0x7c, 0xe7, 0x20,
+	0x6b, 0x3d, 0x7b, 0xe6, 0x12, 0x6e, 0xc5, 0x0c, 0xf6, 0x47, 0x95, 0x7f, 0xfa, 0x9d, 0x0c, 0xb3,
+	0xd5, 0x3b, 0x90, 0xb2, 0x2d, 0x37, 0xc1, 0xd3, 0x82, 0xc2, 0x9a, 0x2e, 0x23, 0xe4, 0xa7, 0x28,
+	0x6e, 0xa9, 0x58, 0x32, 0x4a, 0x4d, 0x36, 0xdf, 0x8c, 0x21, 0x85, 0xee, 0xc0, 0x62, 0x68, 0x3e,
+	0xca, 0x4e, 0x66, 0x22, 0x3b, 0xe0, 0x1b, 0x6f, 0xdf, 0x72, 0x29, 0x07, 0x3a, 0xb1, 0xbd, 0x36,
+	0x2b, 0x8e, 0x32, 0x98, 0x0f, 0x62, 0x36, 0xcd, 0x8d, 0xdb, 0xd4, 0x17, 0xfd, 0x73, 0xb7, 0xe9,
+	0x3d, 0xde, 0x3a, 0xb1, 0x56, 0x0c, 0x7d, 0x71, 0xf8, 0x0d, 0x6a, 0x4a, 0x3e, 0x0c, 0x30, 0x95,
+	0x27, 0x90, 0xf3, 0x35, 0x70, 0xcc, 0xce, 0xd0, 0x85, 0xf4, 0x81, 0x4b, 0x1c, 0x74, 0x32, 0x8c,
+	0x59, 0x89, 0x05, 0x67, 0x19, 0xf2, 0x3d, 0x97, 0x38, 0xa6, 0xda, 0x0d, 0xac, 0x1e, 0x8e, 0xd1,
+	0xd7, 0x12, 0x2e, 0x9c, 0x72, 0x95, 0xbf, 0x1b, 0x56, 0x83, 0x77, 0x43, 0xc6, 0x07, 0x7b, 0x58,
+	0x8c, 0xb0, 0x51, 0xf9, 0xb7, 0x08, 0xb9, 0x7d, 0xc7, 0x62, 0xf5, 0x65, 0xfc, 0x48, 0x04, 0xe9,
+	0xc8, 0x71, 0xec, 0x37, 0xba, 0x0c, 0x60, 0xf7, 0x0e, 0x3b, 0x86, 0xc6, 0x1e, 0x17, 0xb9, 0xa7,
+	0x49, 0x7c, 0xe6, 0x7d, 0x32, 0xa0, 0xcb, 0x2e, 0xd1, 0x1c, 0xc2, 0xdf, 0x1e, 0xd3, 0x7c, 0x99,
+	0xcf, 0xd0, 0xe5, 0x55, 0x28, 0xaa, 0x3d, 0xaf, 0xad, 0x7c, 0x4c, 0x0e, 0xdb, 0x96, 0x75, 0xa4,
+	0xf4, 0x9c, 0x8e, 0xff, 0xc5, 0xef, 0x24, 0x9d, 0x7f, 0xc2, 0xa7, 0x0f, 0x9c, 0x0e, 0x5a, 0x87,
+	0x33, 0x23, 0xc8, 0x2e, 0xf1, 0xda, 0x96, 0xee, 0x96, 0xb2, 0x2b, 0xa9, 0x55, 0x09, 0xa3, 0x08,
+	0xfa, 0x11, 0x5f, 0x41, 0xdf, 0x80, 0x8b, 0xfe, 0x3b, 0x9b, 0x4e, 0x54, 0xcd, 0x33, 0xfa, 0xaa,
+	0x47, 0x14, 0xaf, 0xed, 0x10, 0xb7, 0x6d, 0x75, 0x74, 0x56, 0x76, 0x4b, 0xf8, 0x02, 0x87, 0x6c,
+	0x85, 0x88, 0x56, 0x00, 0x88, 0x29, 0x31, 0xff, 0x1a, 0x4a, 0xa4, 0xa4, 0x91, 0x14, 0x2d, 0xbd,
+	0x9a, 0x74, 0x98, 0xa7, 0x7f, 0x9c, 0x82, 0x73, 0x07, 0x74, 0xa4, 0x1e, 0x76, 0x88, 0x6f, 0x88,
+	0xf7, 0x0c, 0xd2, 0xd1, 0x5d, 0xb4, 0xee, 0xab, 0x5f, 0xf0, 0xbf, 0xa5, 0xc4, 0xf7, 0x6b, 0x7a,
+	0x8e, 0x61, 0x3e, 0x67, 0x25, 0x89, 0x6f, 0x9c, 0xf7, 0x12, 0xd4, 0x2b, 0xce, 0x40, 0x1d, 0x57,
+	0xfe, 0xb3, 0x09, 0xca, 0xe7, 0x9e, 0x75, 0x27, 0xe2, 0xdb, 0xc9, 0xac, 0x57, 0x6b, 0x63, 0xe6,
+	0x49, 0x34, 0xd9, 0x77, 0xa6, 0x9b, 0x2c, 0x3d, 0x03, 0xeb, 0x93, 0x0d, 0x5a, 0xae, 0x02, 0x1a,
+	0xe7, 0x83, 0xbf, 0xf5, 0x72, 0x71, 0x04, 0xe6, 0x4b, 0xc1, 0xb0, 0xf2, 0x7d, 0x11, 0x96, 0xb6,
+	0xfc, 0x67, 0xf2, 0x66, 0xaf, 0xdb, 0x55, 0x9d, 0xc1, 0x58, 0x48, 0x8c, 0x3f, 0x6e, 0xc5, 0x5f,
+	0xc5, 0xa5, 0xc8, 0xab, 0xf8, 0xa8, 0x4b, 0xa5, 0x5f, 0xc7, 0xa5, 0xee, 0x43, 0x41, 0xd5, 0x34,
+	0xe2, 0xba, 0xd1, 0xe2, 0x6e, 0x1a, 0x2d, 0x04, 0xf0, 0x31, 0x7f, 0xcc, 0xbe, 0x8e, 0x3f, 0xfe,
+	0x54, 0x80, 0xfc, 0xbe, 0x43, 0x5c, 0x62, 0x6a, 0xec, 0x46, 0xd1, 0x3a, 0x96, 0x76, 0xc4, 0x14,
+	0x90, 0xc1, 0x7c, 0x40, 0x1b, 0x59, 0x6a, 0xf4, 0x92, 0xc8, 0xd2, 0x64, 0xf4, 0x95, 0x32, 0x20,
+	0xac, 0x6e, 0xa9, 0x9e, 0xca, 0x73, 0x38, 0x83, 0x96, 0xbf, 0x02, 0x52, 0x38, 0xf5, 0x3a, 0x9f,
+	0x5e, 0x2a, 0x3b, 0x90, 0xad, 0x33, 0x03, 0x47, 0x2c, 0xb1, 0xc8, 0x2c, 0xb1, 0x06, 0x79, 0xdb,
+	0x3f, 0x2e, 0x21, 0xdd, 0x07, 0x9c, 0xe0, 0x10, 0x54, 0x69, 0x00, 0x0c, 0xff, 0xd8, 0x10, 0x7b,
+	0x76, 0x17, 0x92, 0x9e, 0xdd, 0x47, 0x1f, 0xee, 0xc5, 0xd8, 0xc3, 0x7d, 0xe5, 0x87, 0x02, 0x14,
+	0x22, 0x9f, 0x84, 0x8f, 0xf7, 0x1a, 0x40, 0xff, 0x0f, 0x4b, 0x0e, 0xe9, 0xa8, 0xb4, 0x97, 0x54,
+	0x7c, 0x40, 0x8a, 0x01, 0x4e, 0x06, 0xd3, 0x7b, 0xfc, 0xbe, 0xd0, 0x00, 0x86, 0x3b, 0x47, 0xff,
+	0x2a, 0x20, 0x8c, 0xff, 0x55, 0xe0, 0x12, 0x48, 0x3a, 0xe9, 0xd0, 0x16, 0x95, 0x38, 0x81, 0x40,
+	0xe1, 0xc4, 0xc8, 0x1f, 0x09, 0x52, 0xa3, 0x7f, 0x24, 0xf8, 0x89, 0x00, 0xf9, 0x2d, 0x4b, 0x93,
+	0xfb, 0xd4, 0x12, 0xb7, 0x46, 0xda, 0xa3, 0xf3, 0x11, 0x11, 0x03, 0x48, 0xa4, 0x43, 0x5a, 0x03,
+	0x7e, 0x3b, 0xb8, 0x6d, 0xff, 0xc8, 0xd8, 0x5f, 0x43, 0x98, 0x3a, 0xf1, 0x10, 0x83, 0xae, 0x40,
+	0xf8, 0x5f, 0x95, 0x80, 0x11, 0x09, 0x43, 0x30, 0xb5, 0xa3, 0xdf, 0xfc, 0x54, 0x04, 0x29, 0xec,
+	0xc3, 0xd0, 0x69, 0x58, 0x7a, 0x5c, 0xdb, 0x3d, 0x90, 0x95, 0xd6, 0xd3, 0x7d, 0x59, 0x69, 0x1c,
+	0xec, 0xee, 0x16, 0x17, 0xd0, 0x39, 0x40, 0x91, 0xc9, 0xcd, 0xbd, 0xbd, 0x5d, 0xb9, 0xd6, 0x28,
+	0x0a, 0xb1, 0xf9, 0x9d, 0x46, 0x4b, 0x7e, 0x20, 0xe3, 0xa2, 0x18, 0xdb, 0x64, 0x77, 0xaf, 0xf1,
+	0xa0, 0x98, 0x42, 0x67, 0xe1, 0x54, 0x64, 0x72, 0x6b, 0xef, 0x60, 0x73, 0x57, 0x2e, 0xa6, 0x63,
+	0xd3, 0xcd, 0x16, 0xde, 0x69, 0x3c, 0x28, 0x66, 0xd0, 0x19, 0x28, 0x46, 0x8f, 0x7c, 0xda, 0x92,
+	0x9b, 0xc5, 0x6c, 0x6c, 0xe3, 0xad, 0x5a, 0x4b, 0x2e, 0xe6, 0x50, 0x19, 0xce, 0x45, 0x26, 0x69,
+	0x57, 0xa0, 0xec, 0x6d, 0x3e, 0x94, 0xeb, 0xad, 0x62, 0x1e, 0x5d, 0x80, 0xb3, 0xf1, 0xb5, 0x1a,
+	0xc6, 0xb5, 0xa7, 0x45, 0x29, 0xb6, 0x57, 0x4b, 0xfe, 0x76, 0xab, 0x08, 0xb1, 0xbd, 0x7c, 0x89,
+	0x94, 0x7a, 0xa3, 0x55, 0x2c, 0xa0, 0xf3, 0x70, 0x3a, 0x26, 0x15, 0x5b, 0x58, 0x8c, 0xef, 0x84,
+	0x65, 0xb9, 0x78, 0xe2, 0xe6, 0x2f, 0x04, 0x58, 0x8c, 0xda, 0x0f, 0xbd, 0x03, 0x2b, 0x5b, 0x7b,
+	0x75, 0x45, 0x7e, 0x2c, 0x37, 0x5a, 0x81, 0x0e, 0xea, 0x07, 0x8f, 0xe4, 0x46, 0xab, 0xa9, 0xd4,
+	0xb7, 0x6b, 0x8d, 0x07, 0xf2, 0x56, 0x71, 0x61, 0x2a, 0xea, 0x49, 0xad, 0x55, 0xdf, 0x96, 0xb7,
+	0x8a, 0x02, 0xba, 0x0e, 0x95, 0x89, 0xa8, 0x83, 0x46, 0x80, 0x13, 0xd1, 0x35, 0xb8, 0x12, 0xc3,
+	0xed, 0x63, 0xb9, 0x29, 0x37, 0xea, 0x72, 0x78, 0x64, 0x6a, 0xf3, 0xd6, 0x6f, 0x5e, 0x2e, 0x0b,
+	0x7f, 0x78, 0xb9, 0x2c, 0xfc, 0xf9, 0xe5, 0xb2, 0xf0, 0xf3, 0xbf, 0x2c, 0x2f, 0xc0, 0x29, 0x9d,
+	0xf4, 0x03, 0xa7, 0x52, 0x6d, 0xa3, 0xda, 0xbf, 0xbd, 0x2f, 0x7c, 0x90, 0xae, 0xde, 0xef, 0xdf,
+	0x3e, 0xcc, 0xb2, 0x74, 0xf7, 0xa5, 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0x9e, 0x7c, 0x09, 0xa2,
+	0x8d, 0x25, 0x00, 0x00,
 }
 
 func (m *ChangePack) Marshal() (dAtA []byte, err error) {
@@ -4453,10 +4503,10 @@ func (m *Operation_TreeEdit) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		i--
 		dAtA[i] = 0x2a
 	}
-	if len(m.Content) > 0 {
-		for iNdEx := len(m.Content) - 1; iNdEx >= 0; iNdEx-- {
+	if len(m.Contents) > 0 {
+		for iNdEx := len(m.Contents) - 1; iNdEx >= 0; iNdEx-- {
 			{
-				size, err := m.Content[iNdEx].MarshalToSizedBuffer(dAtA[:i])
+				size, err := m.Contents[iNdEx].MarshalToSizedBuffer(dAtA[:i])
 				if err != nil {
 					return 0, err
 				}
@@ -5686,6 +5736,47 @@ func (m *TreeNode) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	return len(dAtA) - i, nil
 }
 
+func (m *TreeNodes) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *TreeNodes) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *TreeNodes) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.XXX_unrecognized != nil {
+		i -= len(m.XXX_unrecognized)
+		copy(dAtA[i:], m.XXX_unrecognized)
+	}
+	if len(m.Content) > 0 {
+		for iNdEx := len(m.Content) - 1; iNdEx >= 0; iNdEx-- {
+			{
+				size, err := m.Content[iNdEx].MarshalToSizedBuffer(dAtA[:i])
+				if err != nil {
+					return 0, err
+				}
+				i -= size
+				i = encodeVarintResources(dAtA, i, uint64(size))
+			}
+			i--
+			dAtA[i] = 0xa
+		}
+	}
+	return len(dAtA) - i, nil
+}
+
 func (m *TreePos) Marshal() (dAtA []byte, err error) {
 	size := m.Size()
 	dAtA = make([]byte, size)
@@ -6859,8 +6950,8 @@ func (m *Operation_TreeEdit) Size() (n int) {
 		l = m.To.Size()
 		n += 1 + l + sovResources(uint64(l))
 	}
-	if len(m.Content) > 0 {
-		for _, e := range m.Content {
+	if len(m.Contents) > 0 {
+		for _, e := range m.Contents {
 			l = e.Size()
 			n += 1 + l + sovResources(uint64(l))
 		}
@@ -7379,6 +7470,24 @@ func (m *TreeNode) Size() (n int) {
 	return n
 }
 
+func (m *TreeNodes) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if len(m.Content) > 0 {
+		for _, e := range m.Content {
+			l = e.Size()
+			n += 1 + l + sovResources(uint64(l))
+		}
+	}
+	if m.XXX_unrecognized != nil {
+		n += len(m.XXX_unrecognized)
+	}
+	return n
+}
+
 func (m *TreePos) Size() (n int) {
 	if m == nil {
 		return 0
@@ -10665,7 +10774,7 @@ func (m *Operation_TreeEdit) Unmarshal(dAtA []byte) error {
 			iNdEx = postIndex
 		case 4:
 			if wireType != 2 {
-				return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType)
+				return fmt.Errorf("proto: wrong wireType = %d for field Contents", wireType)
 			}
 			var msglen int
 			for shift := uint(0); ; shift += 7 {
@@ -10692,8 +10801,8 @@ func (m *Operation_TreeEdit) Unmarshal(dAtA []byte) error {
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			m.Content = append(m.Content, &TreeNode{})
-			if err := m.Content[len(m.Content)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+			m.Contents = append(m.Contents, &TreeNodes{})
+			if err := m.Contents[len(m.Contents)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
 				return err
 			}
 			iNdEx = postIndex
@@ -13904,6 +14013,91 @@ func (m *TreeNode) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
+func (m *TreeNodes) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowResources
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: TreeNodes: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: TreeNodes: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowResources
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthResources
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthResources
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Content = append(m.Content, &TreeNode{})
+			if err := m.Content[len(m.Content)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipResources(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthResources
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
 func (m *TreePos) Unmarshal(dAtA []byte) error {
 	l := len(dAtA)
 	iNdEx := 0
diff --git a/api/yorkie/v1/resources.proto b/api/yorkie/v1/resources.proto
index 0955ea0d..d695f363 100644
--- a/api/yorkie/v1/resources.proto
+++ b/api/yorkie/v1/resources.proto
@@ -108,7 +108,7 @@ message Operation {
     TimeTicket parent_created_at = 1;
     TreePos from = 2;
     TreePos to = 3;
-    repeated TreeNode content = 4;
+    repeated TreeNodes contents = 4;
     TimeTicket executed_at = 5;
   }
   message TreeStyle {
@@ -233,6 +233,10 @@ message TreeNode {
   map<string, NodeAttr> attributes = 7;
 }
 
+message TreeNodes {
+  repeated TreeNode content = 1;
+}
+
 message TreePos {
   TimeTicket created_at = 1;
   int32 offset = 2;
diff --git a/pkg/document/crdt/tree.go b/pkg/document/crdt/tree.go
index 7559cb5b..4cb98de2 100644
--- a/pkg/document/crdt/tree.go
+++ b/pkg/document/crdt/tree.go
@@ -473,7 +473,7 @@ func (t *Tree) ToXML() string {
 
 // EditByIndex edits the given range with the given value.
 // This method uses indexes instead of a pair of TreePos for testing.
-func (t *Tree) EditByIndex(start, end int, content *TreeNode, editedAt *time.Ticket) error {
+func (t *Tree) EditByIndex(start, end int, contents []*TreeNode, editedAt *time.Ticket) error {
 	fromPos, err := t.FindPos(start)
 	if err != nil {
 		return err
@@ -483,7 +483,7 @@ func (t *Tree) EditByIndex(start, end int, content *TreeNode, editedAt *time.Tic
 		return err
 	}
 
-	return t.Edit(fromPos, toPos, content, editedAt)
+	return t.Edit(fromPos, toPos, contents, editedAt)
 }
 
 // FindPos finds the position of the given index in the tree.
@@ -501,7 +501,7 @@ func (t *Tree) FindPos(offset int) (*TreePos, error) {
 
 // Edit edits the tree with the given range and content.
 // If the content is undefined, the range will be removed.
-func (t *Tree) Edit(from, to *TreePos, content *TreeNode, editedAt *time.Ticket) error {
+func (t *Tree) Edit(from, to *TreePos, contents []*TreeNode, editedAt *time.Ticket) error {
 	// 01. split text nodes at the given range if needed.
 	toPos, toRight, err := t.findTreePosWithSplitText(to, editedAt)
 	if err != nil {
@@ -567,29 +567,39 @@ func (t *Tree) Edit(from, to *TreePos, content *TreeNode, editedAt *time.Ticket)
 	}
 
 	// 03. insert the given node at the given position.
-	if content != nil {
-		// 03-1. insert the content nodes to the list.
+	if len(contents) != 0 {
+
 		previous := fromRight.Prev
-		index.TraverseNode(content.IndexTreeNode, func(node *index.Node[*TreeNode], depth int) {
-			t.InsertAfter(previous, node.Value)
-			previous = node.Value
-		})
-
-		// 03-2. insert the content nodes to the tree.
-		if fromPos.Node.IsText() {
-			if fromPos.Offset == 0 {
-				if err := fromPos.Node.Parent.InsertBefore(content.IndexTreeNode, fromPos.Node); err != nil {
-					return err
+		offset := fromPos.Offset
+		node := fromPos.Node
+
+		for _, content := range contents {
+			// 03-1. insert the content nodes to the list.
+			index.TraverseNode(content.IndexTreeNode, func(node *index.Node[*TreeNode], depth int) {
+				t.InsertAfter(previous, node.Value)
+				previous = node.Value
+			})
+
+			// 03-2. insert the content nodes to the tree.
+			if node.IsText() {
+				// if `contents` is consist of text nodes, then there'll be only one element in `contents`
+				// thus, there's no need to update fromPos
+				if fromPos.Offset == 0 {
+					if err := node.Parent.InsertBefore(content.IndexTreeNode, node); err != nil {
+						return err
+					}
+				} else {
+					if err := node.Parent.InsertAfter(content.IndexTreeNode, node); err != nil {
+						return err
+					}
 				}
 			} else {
-				if err := fromPos.Node.Parent.InsertAfter(content.IndexTreeNode, fromPos.Node); err != nil {
+				target := node
+				if err := target.InsertAt(content.IndexTreeNode, offset+1); err != nil {
 					return err
 				}
-			}
-		} else {
-			target := fromPos.Node
-			if err := target.InsertAt(content.IndexTreeNode, fromPos.Offset+1); err != nil {
-				return err
+
+				offset++
 			}
 		}
 	}
diff --git a/pkg/document/json/tree.go b/pkg/document/json/tree.go
index dc7710bc..78e33b22 100644
--- a/pkg/document/json/tree.go
+++ b/pkg/document/json/tree.go
@@ -17,6 +17,8 @@
 package json
 
 import (
+	"errors"
+
 	"github.com/yorkie-team/yorkie/pkg/document/change"
 	"github.com/yorkie-team/yorkie/pkg/document/crdt"
 	"github.com/yorkie-team/yorkie/pkg/document/operations"
@@ -29,6 +31,19 @@ const (
 	DefaultRootNodeType = "root"
 )
 
+var (
+	// ErrEmptyTextNode is returned when there's empty string value in text node
+	ErrEmptyTextNode = errors.New("text node cannot have empty value")
+	// ErrMixedNodeType is returned when there're element node and text node inside contents array
+	ErrMixedNodeType = errors.New("element node and text node cannot be passed together")
+	// ErrIndexBoundary is returned when from index is bigger than to index
+	ErrIndexBoundary = errors.New("from should be less than or equal to to")
+	// ErrPathLenDiff is returned when two paths have different length
+	ErrPathLenDiff = errors.New("both paths should have same length")
+	// ErrEmptyPath is returned when there's empty path
+	ErrEmptyPath = errors.New("path should not be empty")
+)
+
 // TreeNode is a node of Tree.
 type TreeNode struct {
 	// Type is the type of this node. It is used to distinguish between text
@@ -62,30 +77,47 @@ func NewTree(ctx *change.Context, tree *crdt.Tree) *Tree {
 	}
 }
 
-// Edit edits this tree with the given node.
-func (t *Tree) Edit(fromIdx, toIdx int, content *TreeNode) bool {
-	if fromIdx > toIdx {
-		panic("from should be less than or equal to to")
+// validateTextNode make sure that text node have non-empty string value
+func validateTextNode(treeNode TreeNode) error {
+	if len(treeNode.Value) == 0 {
+		return ErrEmptyTextNode
 	}
-	ticket := t.context.IssueTimeTicket()
 
-	var node *crdt.TreeNode
-	if content != nil {
-		var attributes *crdt.RHT
-		if content.Attributes != nil {
-			attributes = crdt.NewRHT()
-			for key, val := range content.Attributes {
-				attributes.Set(key, val, ticket)
+	return nil
+}
+
+// validateTextNode make sure that treeNodes consist of only one of the two types (text or element)
+func validateTreeNodes(treeNodes []*TreeNode) error {
+	firstTreeNodeType := treeNodes[0].Type
+
+	if firstTreeNodeType == index.DefaultTextType {
+		for _, treeNode := range treeNodes {
+			if treeNode.Type != index.DefaultTextType {
+				return ErrMixedNodeType
+			}
+
+			if err := validateTextNode(*treeNode); err != nil {
+				return err
 			}
 		}
-		node = crdt.NewTreeNode(crdt.NewTreePos(ticket, 0), content.Type, attributes, content.Value)
-		for _, child := range content.Children {
-			if err := buildDescendants(t.context, child, node); err != nil {
-				panic(err)
+
+	} else {
+		for _, treeNode := range treeNodes {
+			if treeNode.Type == index.DefaultTextType {
+				return ErrMixedNodeType
 			}
 		}
 	}
 
+	return nil
+}
+
+// Edit edits this tree with the given nodes.
+func (t *Tree) Edit(fromIdx, toIdx int, contents ...*TreeNode) bool {
+	if fromIdx > toIdx {
+		panic(ErrIndexBoundary)
+	}
+
 	fromPos, err := t.Tree.FindPos(fromIdx)
 	if err != nil {
 		panic(err)
@@ -95,32 +127,7 @@ func (t *Tree) Edit(fromIdx, toIdx int, content *TreeNode) bool {
 		panic(err)
 	}
 
-	var clone *crdt.TreeNode
-	if node != nil {
-		clone, err = node.DeepCopy()
-		if err != nil {
-			panic(err)
-		}
-	}
-
-	ticket = t.context.LastTimeTicket()
-	if err = t.Tree.Edit(fromPos, toPos, clone, ticket); err != nil {
-		panic(err)
-	}
-
-	t.context.Push(operations.NewTreeEdit(
-		t.CreatedAt(),
-		fromPos,
-		toPos,
-		node,
-		ticket,
-	))
-
-	if fromPos.CreatedAt.Compare(toPos.CreatedAt) != 0 || fromPos.Offset != toPos.Offset {
-		t.context.RegisterElementHasRemovedNodes(t.Tree)
-	}
-
-	return true
+	return t.edit(fromPos, toPos, contents)
 }
 
 // Len returns the length of this tree.
@@ -128,46 +135,65 @@ func (t *Tree) Len() int {
 	return t.IndexTree.Root().Len()
 }
 
-// EditByPath edits this tree with the given path and node.
-func (t *Tree) EditByPath(fromPath []int, toPath []int, content *TreeNode) bool {
+// edit edits the tree with the given nodes
+func (t *Tree) edit(fromPos, toPos *crdt.TreePos, contents []*TreeNode) bool {
 	ticket := t.context.IssueTimeTicket()
 
-	var node *crdt.TreeNode
-	if content != nil {
-		var attributes *crdt.RHT
-		if content.Attributes != nil {
-			attributes = crdt.NewRHT()
-			for key, val := range content.Attributes {
-				attributes.Set(key, val, ticket)
-			}
+	var nodes []*crdt.TreeNode
+
+	if len(contents) != 0 && contents[0] != nil {
+		if err := validateTreeNodes(contents); err != nil {
+			panic(err)
 		}
-		node = crdt.NewTreeNode(crdt.NewTreePos(ticket, 0), content.Type, attributes, content.Value)
-		for _, child := range content.Children {
-			if err := buildDescendants(t.context, child, node); err != nil {
-				panic(err)
+
+		if contents[0].Type == index.DefaultTextType {
+			value := ""
+
+			for _, content := range contents {
+				value += content.Value
+			}
+
+			nodes = append(nodes, crdt.NewTreeNode(crdt.NewTreePos(ticket, 0), index.DefaultTextType, nil, value))
+		} else {
+			for _, content := range contents {
+				var attributes *crdt.RHT
+				if content.Attributes != nil {
+					attributes = crdt.NewRHT()
+					for key, val := range content.Attributes {
+						attributes.Set(key, val, ticket)
+					}
+				}
+				var node *crdt.TreeNode
+
+				node = crdt.NewTreeNode(crdt.NewTreePos(ticket, 0), content.Type, attributes, content.Value)
+
+				for _, child := range content.Children {
+					if err := buildDescendants(t.context, child, node); err != nil {
+						panic(err)
+					}
+				}
+
+				nodes = append(nodes, node)
 			}
 		}
 	}
 
-	fromPos, err := t.Tree.PathToPos(fromPath)
-	if err != nil {
-		panic(err)
-	}
-	toPos, err := t.Tree.PathToPos(toPath)
-	if err != nil {
-		panic(err)
-	}
+	var clones []*crdt.TreeNode
+	if len(nodes) != 0 {
+		for _, node := range nodes {
+			var clone *crdt.TreeNode
 
-	var clone *crdt.TreeNode
-	if node != nil {
-		clone, err = node.DeepCopy()
-		if err != nil {
-			panic(err)
+			clone, err := node.DeepCopy()
+			if err != nil {
+				panic(err)
+			}
+
+			clones = append(clones, clone)
 		}
 	}
 
 	ticket = t.context.LastTimeTicket()
-	if err = t.Tree.Edit(fromPos, toPos, clone, ticket); err != nil {
+	if err := t.Tree.Edit(fromPos, toPos, clones, ticket); err != nil {
 		panic(err)
 	}
 
@@ -175,7 +201,7 @@ func (t *Tree) EditByPath(fromPath []int, toPath []int, content *TreeNode) bool
 		t.CreatedAt(),
 		fromPos,
 		toPos,
-		node,
+		nodes,
 		ticket,
 	))
 
@@ -186,6 +212,28 @@ func (t *Tree) EditByPath(fromPath []int, toPath []int, content *TreeNode) bool
 	return true
 }
 
+// EditByPath edits this tree with the given path and nodes.
+func (t *Tree) EditByPath(fromPath []int, toPath []int, contents ...*TreeNode) bool {
+	if len(fromPath) != len(toPath) {
+		panic(ErrPathLenDiff)
+	}
+
+	if len(fromPath) == 0 || len(toPath) == 0 {
+		panic(ErrEmptyPath)
+	}
+
+	fromPos, err := t.Tree.PathToPos(fromPath)
+	if err != nil {
+		panic(err)
+	}
+	toPos, err := t.Tree.PathToPos(toPath)
+	if err != nil {
+		panic(err)
+	}
+
+	return t.edit(fromPos, toPos, contents)
+}
+
 // Style sets the attributes to the elements of the given range.
 func (t *Tree) Style(fromIdx, toIdx int, attributes map[string]string) bool {
 	if fromIdx > toIdx {
@@ -237,6 +285,11 @@ func buildRoot(ctx *change.Context, node *TreeNode, createdAt *time.Ticket) *crd
 // buildDescendants converts the given node to a CRDT-based tree node.
 func buildDescendants(ctx *change.Context, n TreeNode, parent *crdt.TreeNode) error {
 	if n.Type == index.DefaultTextType {
+
+		if err := validateTextNode(n); err != nil {
+			return err
+		}
+
 		treeNode := crdt.NewTreeNode(crdt.NewTreePos(ctx.IssueTimeTicket(), 0), n.Type, nil, n.Value)
 		return parent.Append(treeNode)
 	}
diff --git a/pkg/document/operations/tree_edit.go b/pkg/document/operations/tree_edit.go
index aeb4e8c6..845c0245 100644
--- a/pkg/document/operations/tree_edit.go
+++ b/pkg/document/operations/tree_edit.go
@@ -33,8 +33,8 @@ type TreeEdit struct {
 	// toPos represents the end point of the editing range.
 	to *crdt.TreePos
 
-	// content is the content of tree added when editing.
-	content *crdt.TreeNode
+	// contents is the content of tree added when editing.
+	contents []*crdt.TreeNode
 
 	// executedAt is the time the operation was executed.
 	executedAt *time.Ticket
@@ -45,14 +45,14 @@ func NewTreeEdit(
 	parentCreatedAt *time.Ticket,
 	from *crdt.TreePos,
 	to *crdt.TreePos,
-	content *crdt.TreeNode,
+	contents []*crdt.TreeNode,
 	executedAt *time.Ticket,
 ) *TreeEdit {
 	return &TreeEdit{
 		parentCreatedAt: parentCreatedAt,
 		from:            from,
 		to:              to,
-		content:         content,
+		contents:        contents,
 		executedAt:      executedAt,
 	}
 }
@@ -63,15 +63,22 @@ func (e *TreeEdit) Execute(root *crdt.Root) error {
 
 	switch obj := parent.(type) {
 	case *crdt.Tree:
-		var content *crdt.TreeNode
+		var contents []*crdt.TreeNode
 		var err error
-		if e.Content() != nil {
-			content, err = e.Content().DeepCopy()
-			if err != nil {
-				return err
+		if len(e.Contents()) != 0 {
+			for _, content := range e.Contents() {
+				var clone *crdt.TreeNode
+
+				clone, err = content.DeepCopy()
+				if err != nil {
+					return err
+				}
+
+				contents = append(contents, clone)
 			}
+
 		}
-		if err = obj.Edit(e.from, e.to, content, e.executedAt); err != nil {
+		if err = obj.Edit(e.from, e.to, contents, e.executedAt); err != nil {
 			return err
 		}
 
@@ -110,7 +117,7 @@ func (e *TreeEdit) ParentCreatedAt() *time.Ticket {
 	return e.parentCreatedAt
 }
 
-// Content returns the content of Edit.
-func (e *TreeEdit) Content() *crdt.TreeNode {
-	return e.content
+// Contents returns the content of Edit.
+func (e *TreeEdit) Contents() []*crdt.TreeNode {
+	return e.contents
 }


Test patch:
diff --git a/pkg/document/crdt/tree_test.go b/pkg/document/crdt/tree_test.go
index 621671fc..a7e028f5 100644
--- a/pkg/document/crdt/tree_test.go
+++ b/pkg/document/crdt/tree_test.go
@@ -105,7 +105,8 @@ func TestTree(t *testing.T) {
 
 		//           1
 		// <root> <p> </p> </root>
-		err := tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		err := tree.EditByIndex(0, 0,
+			[]*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil)}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
 		assert.Equal(t, "<r><p></p></r>", tree.ToXML())
 		helper.ListEqual(t, tree, []string{"p", "r"})
@@ -115,7 +116,7 @@ func TestTree(t *testing.T) {
 		// <root> <p> h e l l o </p> </root>
 		err = tree.EditByIndex(
 			1, 1,
-			crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "hello"),
+			[]*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "hello")},
 			helper.IssueTime(ctx),
 		)
 		assert.NoError(t, err)
@@ -128,7 +129,7 @@ func TestTree(t *testing.T) {
 		p := crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil)
 		err = p.InsertAt(crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "world"), 0)
 		assert.NoError(t, err)
-		err = tree.EditByIndex(7, 7, p, helper.IssueTime(ctx))
+		err = tree.EditByIndex(7, 7, []*crdt.TreeNode{p}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
 		assert.Equal(t, "<r><p>hello</p><p>world</p></r>", tree.ToXML())
 		helper.ListEqual(t, tree, []string{"text.hello", "p", "text.world", "p", "r"})
@@ -138,7 +139,7 @@ func TestTree(t *testing.T) {
 		// <root> <p> h e l l o ! </p> <p> w  o  r  l  d  </p>  </root>
 		err = tree.EditByIndex(
 			6, 6,
-			crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "!"),
+			[]*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "!")},
 			helper.IssueTime(ctx),
 		)
 		assert.NoError(t, err)
@@ -173,7 +174,7 @@ func TestTree(t *testing.T) {
 		// <root> <p> h e l l o ~ ! </p> <p>  w  o  r  l  d  </p>  </root>
 		err = tree.EditByIndex(
 			6, 6,
-			crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "~"),
+			[]*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "~")},
 			helper.IssueTime(ctx),
 		)
 		assert.NoError(t, err)
@@ -187,14 +188,19 @@ func TestTree(t *testing.T) {
 		// <root> <p> a b </p> <p> c d </p> </root>
 
 		ctx := helper.TextChangeContext(helper.TestRoot())
-		tree := crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx))
-		err := tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		tree := crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx),
+			"root", nil), helper.IssueTime(ctx))
+		err := tree.EditByIndex(0, 0, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"p", nil)}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
+		err = tree.EditByIndex(1, 1, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"text", nil, "ab")}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(4, 4, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		err = tree.EditByIndex(4, 4, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"p", nil)}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(5, 5, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "cd"), helper.IssueTime(ctx))
+		err = tree.EditByIndex(5, 5, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"text", nil, "cd")}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
 		assert.Equal(t, "<root><p>ab</p><p>cd</p></root>", tree.ToXML())
 		helper.ListEqual(t, tree, []string{"text.ab", "p", "text.cd", "p", "root"})
@@ -225,13 +231,17 @@ func TestTree(t *testing.T) {
 
 		ctx := helper.TextChangeContext(helper.TestRoot())
 		tree := crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx))
-		err := tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		err := tree.EditByIndex(0, 0, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"p", nil)}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
+		err = tree.EditByIndex(1, 1, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"text", nil, "ab")}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(4, 4, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		err = tree.EditByIndex(4, 4, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"p", nil)}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(5, 5, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "cd"), helper.IssueTime(ctx))
+		err = tree.EditByIndex(5, 5, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"text", nil, "cd")}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
 		assert.Equal(t, "<root><p>ab</p><p>cd</p></root>", tree.ToXML())
 		helper.ListEqual(t, tree, []string{"text.ab", "p", "text.cd", "p", "root"})
@@ -251,7 +261,8 @@ func TestTree(t *testing.T) {
 		assert.Equal(t, 1, structure.Children[0].Children[1].Size)
 
 		// 03. insert a new text node at the start of the first paragraph.
-		err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "@"), helper.IssueTime(ctx))
+		err = tree.EditByIndex(1, 1, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"text", nil, "@")}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
 		assert.Equal(t, "<root><p>@ad</p></root>", tree.ToXML())
 	})
@@ -262,13 +273,17 @@ func TestTree(t *testing.T) {
 		// <root> <p> <b> <i> a b </i> </b> </p> </root>
 		ctx := helper.TextChangeContext(helper.TestRoot())
 		tree := crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx))
-		err := tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		err := tree.EditByIndex(0, 0, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"p", nil)}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "b", nil), helper.IssueTime(ctx))
+		err = tree.EditByIndex(1, 1, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"b", nil)}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(2, 2, crdt.NewTreeNode(helper.IssuePos(ctx), "i", nil), helper.IssueTime(ctx))
+		err = tree.EditByIndex(2, 2, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"i", nil)}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(3, 3, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
+		err = tree.EditByIndex(3, 3, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"text", nil, "ab")}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
 		assert.Equal(t, "<root><p><b><i>ab</i></b></p></root>", tree.ToXML())
 		err = tree.EditByIndex(5, 6, nil, helper.IssueTime(ctx))
@@ -277,13 +292,17 @@ func TestTree(t *testing.T) {
 
 		// 02. Edit between two element nodes in same hierarchy.
 		tree = crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx))
-		err = tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		err = tree.EditByIndex(0, 0, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"p", nil)}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "b", nil), helper.IssueTime(ctx))
+		err = tree.EditByIndex(1, 1, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"b", nil)}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(2, 2, crdt.NewTreeNode(helper.IssuePos(ctx), "i", nil), helper.IssueTime(ctx))
+		err = tree.EditByIndex(2, 2, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"i", nil)}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(3, 3, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
+		err = tree.EditByIndex(3, 3, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"text", nil, "ab")}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
 		assert.Equal(t, "<root><p><b><i>ab</i></b></p></root>", tree.ToXML())
 		err = tree.EditByIndex(6, 7, nil, helper.IssueTime(ctx))
@@ -292,13 +311,17 @@ func TestTree(t *testing.T) {
 
 		// 03. Edit between text and element node in same hierarchy.
 		tree = crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx))
-		err = tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		err = tree.EditByIndex(0, 0, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"p", nil)}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "b", nil), helper.IssueTime(ctx))
+		err = tree.EditByIndex(1, 1, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"b", nil)}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(2, 2, crdt.NewTreeNode(helper.IssuePos(ctx), "i", nil), helper.IssueTime(ctx))
+		err = tree.EditByIndex(2, 2, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"i", nil)}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(3, 3, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
+		err = tree.EditByIndex(3, 3, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"text", nil, "ab")}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
 		assert.Equal(t, "<root><p><b><i>ab</i></b></p></root>", tree.ToXML())
 		err = tree.EditByIndex(4, 6, nil, helper.IssueTime(ctx))
@@ -307,13 +330,17 @@ func TestTree(t *testing.T) {
 
 		// 04. Edit between text and element node in same hierarchy.
 		tree = crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx))
-		err = tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		err = tree.EditByIndex(0, 0, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"p", nil)}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "b", nil), helper.IssueTime(ctx))
+		err = tree.EditByIndex(1, 1, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"b", nil)}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(2, 2, crdt.NewTreeNode(helper.IssuePos(ctx), "i", nil), helper.IssueTime(ctx))
+		err = tree.EditByIndex(2, 2, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"i", nil)}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(3, 3, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
+		err = tree.EditByIndex(3, 3, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"text", nil, "ab")}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
 		assert.Equal(t, "<root><p><b><i>ab</i></b></p></root>", tree.ToXML())
 		err = tree.EditByIndex(5, 7, nil, helper.IssueTime(ctx))
@@ -322,13 +349,17 @@ func TestTree(t *testing.T) {
 
 		// 05. Edit between text and element node in same hierarchy.
 		tree = crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx))
-		err = tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		err = tree.EditByIndex(0, 0, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"p", nil)}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "b", nil), helper.IssueTime(ctx))
+		err = tree.EditByIndex(1, 1, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"b", nil)}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(2, 2, crdt.NewTreeNode(helper.IssuePos(ctx), "i", nil), helper.IssueTime(ctx))
+		err = tree.EditByIndex(2, 2, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"i", nil)}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(3, 3, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
+		err = tree.EditByIndex(3, 3, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"text", nil, "ab")}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
 		assert.Equal(t, "<root><p><b><i>ab</i></b></p></root>", tree.ToXML())
 		err = tree.EditByIndex(4, 7, nil, helper.IssueTime(ctx))
@@ -337,13 +368,17 @@ func TestTree(t *testing.T) {
 
 		// 06. Edit between text and element node in same hierarchy.
 		tree = crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx))
-		err = tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		err = tree.EditByIndex(0, 0, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"p", nil)}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "b", nil), helper.IssueTime(ctx))
+		err = tree.EditByIndex(1, 1, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"b", nil)}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(2, 2, crdt.NewTreeNode(helper.IssuePos(ctx), "i", nil), helper.IssueTime(ctx))
+		err = tree.EditByIndex(2, 2, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"i", nil)}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(3, 3, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
+		err = tree.EditByIndex(3, 3, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"text", nil, "ab")}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
 		assert.Equal(t, "<root><p><b><i>ab</i></b></p></root>", tree.ToXML())
 		err = tree.EditByIndex(3, 7, nil, helper.IssueTime(ctx))
@@ -352,19 +387,26 @@ func TestTree(t *testing.T) {
 
 		// 07. Edit between text and element node in same hierarchy.
 		tree = crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx))
-		err = tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		err = tree.EditByIndex(0, 0, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"p", nil)}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
+		err = tree.EditByIndex(1, 1, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"text", nil, "ab")}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(4, 4, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		err = tree.EditByIndex(4, 4, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"p", nil)}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(5, 5, crdt.NewTreeNode(helper.IssuePos(ctx), "b", nil), helper.IssueTime(ctx))
+		err = tree.EditByIndex(5, 5, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"b", nil)}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(6, 6, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "cd"), helper.IssueTime(ctx))
+		err = tree.EditByIndex(6, 6, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"text", nil, "cd")}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(10, 10, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		err = tree.EditByIndex(10, 10, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"p", nil)}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(11, 11, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ef"), helper.IssueTime(ctx))
+		err = tree.EditByIndex(11, 11, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"text", nil, "ef")}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
 		assert.Equal(t, "<root><p>ab</p><p><b>cd</b></p><p>ef</p></root>", tree.ToXML())
 		err = tree.EditByIndex(9, 10, nil, helper.IssueTime(ctx))
@@ -376,13 +418,17 @@ func TestTree(t *testing.T) {
 		// 01. style attributes to an element node.
 		ctx := helper.TextChangeContext(helper.TestRoot())
 		tree := crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx))
-		err := tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		err := tree.EditByIndex(0, 0, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"p", nil)}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
+		err = tree.EditByIndex(1, 1, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"text", nil, "ab")}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(4, 4, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		err = tree.EditByIndex(4, 4, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"p", nil)}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
-		err = tree.EditByIndex(5, 5, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "cd"), helper.IssueTime(ctx))
+		err = tree.EditByIndex(5, 5, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx),
+			"text", nil, "cd")}, helper.IssueTime(ctx))
 		assert.NoError(t, err)
 		assert.Equal(t, "<root><p>ab</p><p>cd</p></root>", tree.ToXML())
 
diff --git a/test/integration/tree_test.go b/test/integration/tree_test.go
index 9d803e3d..beceb8f4 100644
--- a/test/integration/tree_test.go
+++ b/test/integration/tree_test.go
@@ -239,6 +239,52 @@ func TestTree(t *testing.T) {
 		assert.NoError(t, err)
 	})
 
+	t.Run("edit its content when multi tree nodes passed", func(t *testing.T) {
+		doc := document.New(helper.TestDocKey(t))
+		err := doc.Update(func(root *json.Object) error {
+			root.SetNewTree("t", &json.TreeNode{
+				Type: "doc",
+				Children: []json.TreeNode{{
+					Type: "tc",
+					Children: []json.TreeNode{{
+						Type: "p", Children: []json.TreeNode{{
+							Type: "tn", Children: []json.TreeNode{{
+								Type: "text", Value: "ab",
+							}},
+						}},
+					}},
+				}},
+			})
+			assert.Equal(t, "<doc><tc><p><tn>ab</tn></p></tc></doc>", root.GetTree("t").ToXML())
+
+			root.GetTree("t").EditByPath([]int{0, 0, 0, 1}, []int{0, 0, 0, 1}, &json.TreeNode{
+				Type:  "text",
+				Value: "X"}, &json.TreeNode{
+				Type:  "text",
+				Value: "X",
+			})
+			assert.Equal(t, "<doc><tc><p><tn>aXXb</tn></p></tc></doc>", root.GetTree("t").ToXML())
+
+			root.GetTree("t").EditByPath([]int{0, 1}, []int{0, 1}, &json.TreeNode{
+				Type:     "p",
+				Children: []json.TreeNode{{Type: "tn", Children: []json.TreeNode{{Type: "text", Value: "te"}, {Type: "text", Value: "st"}}}}},
+				&json.TreeNode{
+					Type:     "p",
+					Children: []json.TreeNode{{Type: "tn", Children: []json.TreeNode{{Type: "text", Value: "te"}, {Type: "text", Value: "xt"}}}},
+				})
+			assert.Equal(t, "<doc><tc><p><tn>aXXb</tn></p><p><tn>test</tn></p><p><tn>text</tn></p></tc></doc>", root.GetTree("t").ToXML())
+
+			root.GetTree("t").EditByPath([]int{0, 3}, []int{0, 3}, &json.TreeNode{
+				Type:     "p",
+				Children: []json.TreeNode{{Type: "tn", Children: []json.TreeNode{{Type: "text", Value: "te"}, {Type: "text", Value: "st"}}}}},
+				&json.TreeNode{Type: "tn", Children: []json.TreeNode{{Type: "text", Value: "te"}, {Type: "text", Value: "xt"}}})
+			assert.Equal(t, "<doc><tc><p><tn>aXXb</tn></p><p><tn>test</tn></p><p><tn>text</tn></p><p><tn>test</tn></p><tn>text</tn></tc></doc>", root.GetTree("t").ToXML())
+
+			return nil
+		})
+		assert.NoError(t, err)
+	})
+
 	t.Run("edit its content with attributes test", func(t *testing.T) {
 		doc := document.New(helper.TestDocKey(t))
 		err := doc.Update(func(root *json.Object) error {