diff --git a/api/converter/from_pb.go b/api/converter/from_pb.go index cbb7bfc50..5194b171e 100644 --- a/api/converter/from_pb.go +++ b/api/converter/from_pb.go @@ -438,9 +438,6 @@ func fromStyle(pbStyle *api.Operation_Style) (*operations.Style, error) { if err != nil { return nil, err } - createdAtMapByActor, err := fromCreatedAtMapByActor( - pbStyle.CreatedAtMapByActor, - ) if err != nil { return nil, err } @@ -452,7 +449,6 @@ func fromStyle(pbStyle *api.Operation_Style) (*operations.Style, error) { parentCreatedAt, from, to, - createdAtMapByActor, pbStyle.Attributes, executedAt, ), nil diff --git a/api/converter/to_pb.go b/api/converter/to_pb.go index a61f553aa..74341bff2 100644 --- a/api/converter/to_pb.go +++ b/api/converter/to_pb.go @@ -356,12 +356,11 @@ func toEdit(e *operations.Edit) (*api.Operation_Edit_, error) { func toStyle(style *operations.Style) (*api.Operation_Style_, error) { return &api.Operation_Style_{ Style: &api.Operation_Style{ - ParentCreatedAt: ToTimeTicket(style.ParentCreatedAt()), - From: toTextNodePos(style.From()), - To: toTextNodePos(style.To()), - CreatedAtMapByActor: toCreatedAtMapByActor(style.MaxCreatedAtMapByActor()), - Attributes: style.Attributes(), - ExecutedAt: ToTimeTicket(style.ExecutedAt()), + ParentCreatedAt: ToTimeTicket(style.ParentCreatedAt()), + From: toTextNodePos(style.From()), + To: toTextNodePos(style.To()), + Attributes: style.Attributes(), + ExecutedAt: ToTimeTicket(style.ExecutedAt()), }, }, nil } diff --git a/pkg/document/crdt/rga_tree_split.go b/pkg/document/crdt/rga_tree_split.go index 2558717b1..f4966eb09 100644 --- a/pkg/document/crdt/rga_tree_split.go +++ b/pkg/document/crdt/rga_tree_split.go @@ -271,8 +271,8 @@ func (s *RGATreeSplitNode[V]) Remove(removedAt *time.Ticket, clientLamportAtChan } // canStyle checks if node is able to set style. -func (s *RGATreeSplitNode[V]) canStyle(editedAt *time.Ticket, maxCreatedAt *time.Ticket) bool { - return !s.createdAt().After(maxCreatedAt) && +func (s *RGATreeSplitNode[V]) canStyle(editedAt *time.Ticket, clientLamportAtChange int64) bool { + return (s.createdAt().Lamport() <= clientLamportAtChange) && (s.removedAt == nil || editedAt.After(s.removedAt)) } diff --git a/pkg/document/crdt/text.go b/pkg/document/crdt/text.go index 2fca219ed..8ba0e0507 100644 --- a/pkg/document/crdt/text.go +++ b/pkg/document/crdt/text.go @@ -292,46 +292,40 @@ func (t *Text) Edit( func (t *Text) Style( from, to *RGATreeSplitNodePos, - maxCreatedAtMapByActor map[string]*time.Ticket, attributes map[string]string, executedAt *time.Ticket, -) (map[string]*time.Ticket, []GCPair, error) { + versionVector time.VersionVector, +) ([]GCPair, error) { // 01. Split nodes with from and to _, toRight, err := t.rgaTreeSplit.findNodeWithSplit(to, executedAt) if err != nil { - return nil, nil, err + return nil, err } _, fromRight, err := t.rgaTreeSplit.findNodeWithSplit(from, executedAt) if err != nil { - return nil, nil, err + return nil, err } // 02. style nodes between from and to nodes := t.rgaTreeSplit.findBetween(fromRight, toRight) - createdAtMapByActor := make(map[string]*time.Ticket) var toBeStyled []*RGATreeSplitNode[*TextValue] for _, node := range nodes { - actorIDHex := node.id.createdAt.ActorIDHex() + actorID := node.id.createdAt.ActorID() - var maxCreatedAt *time.Ticket - if len(maxCreatedAtMapByActor) == 0 { - maxCreatedAt = time.MaxTicket + var clientLamportAtChange int64 + if versionVector == nil { + clientLamportAtChange = time.MaxLamport } else { - createdAt, ok := maxCreatedAtMapByActor[actorIDHex] + lamport, ok := versionVector.Get(actorID) if ok { - maxCreatedAt = createdAt + clientLamportAtChange = lamport } else { - maxCreatedAt = time.InitialTicket + clientLamportAtChange = 0 } } - if node.canStyle(executedAt, maxCreatedAt) { - maxCreatedAt = createdAtMapByActor[actorIDHex] - createdAt := node.id.createdAt - if maxCreatedAt == nil || createdAt.After(maxCreatedAt) { - createdAtMapByActor[actorIDHex] = createdAt - } + if node.canStyle(executedAt, clientLamportAtChange) { toBeStyled = append(toBeStyled, node) } } @@ -349,7 +343,7 @@ func (t *Text) Style( } } - return createdAtMapByActor, pairs, nil + return pairs, nil } // Nodes returns the internal nodes of this Text. diff --git a/pkg/document/crdt/text_test.go b/pkg/document/crdt/text_test.go index 988769182..c129e5390 100644 --- a/pkg/document/crdt/text_test.go +++ b/pkg/document/crdt/text_test.go @@ -80,7 +80,7 @@ func TestText(t *testing.T) { assert.Equal(t, `[{"val":"Hello "},{"val":"Yorkie"}]`, text.Marshal()) fromPos, toPos, _ = text.CreateRange(0, 1) - _, _, err = text.Style(fromPos, toPos, nil, map[string]string{"b": "1"}, ctx.IssueTimeTicket()) + _, err = text.Style(fromPos, toPos, map[string]string{"b": "1"}, ctx.IssueTimeTicket(), nil) assert.NoError(t, err) assert.Equal( t, diff --git a/pkg/document/json/text.go b/pkg/document/json/text.go index 764c44f1b..0083ffb24 100644 --- a/pkg/document/json/text.go +++ b/pkg/document/json/text.go @@ -112,12 +112,12 @@ func (p *Text) Style(from, to int, attributes map[string]string) *Text { } ticket := p.context.IssueTimeTicket() - maxCreationMapByActor, pairs, err := p.Text.Style( + pairs, err := p.Text.Style( fromPos, toPos, - nil, attributes, ticket, + nil, ) if err != nil { panic(err) @@ -131,7 +131,6 @@ func (p *Text) Style(from, to int, attributes map[string]string) *Text { p.CreatedAt(), fromPos, toPos, - maxCreationMapByActor, attributes, ticket, )) diff --git a/pkg/document/operations/style.go b/pkg/document/operations/style.go index 9032275be..adf321344 100644 --- a/pkg/document/operations/style.go +++ b/pkg/document/operations/style.go @@ -32,10 +32,6 @@ type Style struct { // to is the end point of the range to apply the style to. to *crdt.RGATreeSplitNodePos - // maxCreatedAtMapByActor is a map that stores the latest creation time - // by actor for the nodes included in the range to apply the style to. - maxCreatedAtMapByActor map[string]*time.Ticket - // attributes represents the text style. attributes map[string]string @@ -48,29 +44,32 @@ func NewStyle( parentCreatedAt *time.Ticket, from *crdt.RGATreeSplitNodePos, to *crdt.RGATreeSplitNodePos, - maxCreatedAtMapByActor map[string]*time.Ticket, attributes map[string]string, executedAt *time.Ticket, ) *Style { return &Style{ - parentCreatedAt: parentCreatedAt, - from: from, - to: to, - maxCreatedAtMapByActor: maxCreatedAtMapByActor, - attributes: attributes, - executedAt: executedAt, + parentCreatedAt: parentCreatedAt, + from: from, + to: to, + attributes: attributes, + executedAt: executedAt, } } // Execute executes this operation on the given document(`root`). func (e *Style) Execute(root *crdt.Root, opts ...Option) error { + options := &ExecuteOption{} + for _, opt := range opts { + opt(options) + } + parent := root.FindByCreatedAt(e.parentCreatedAt) obj, ok := parent.(*crdt.Text) if !ok { return ErrNotApplicableDataType } - _, pairs, err := obj.Style(e.from, e.to, e.maxCreatedAtMapByActor, e.attributes, e.executedAt) + pairs, err := obj.Style(e.from, e.to, e.attributes, e.executedAt, options.VersionVector) if err != nil { return err } @@ -111,9 +110,3 @@ func (e *Style) ParentCreatedAt() *time.Ticket { func (e *Style) Attributes() map[string]string { return e.attributes } - -// MaxCreatedAtMapByActor returns the map that stores the latest creation time -// by actor for the nodes included in the range to apply the style to. -func (e *Style) MaxCreatedAtMapByActor() map[string]*time.Ticket { - return e.maxCreatedAtMapByActor -}