github.com/ericwq/aprilsh@v0.0.0-20240517091432-958bc568daa0/statesync/user.go (about) 1 // Copyright 2022 wangqi. All rights reserved. 2 // Use of this source code is governed by a MIT-style 3 // license that can be found in the LICENSE file. 4 5 package statesync 6 7 import ( 8 "bytes" 9 "fmt" 10 "reflect" 11 "strings" 12 13 pb "github.com/ericwq/aprilsh/protobufs/user" 14 "github.com/ericwq/aprilsh/terminal" 15 "github.com/rivo/uniseg" 16 "google.golang.org/protobuf/proto" 17 ) 18 19 type UserEventType uint8 20 21 const ( 22 UserByteType UserEventType = iota 23 ResizeType 24 ) 25 26 // UserByte instance is created by aprish client, when client got the user input. 27 // Resize instance is created by aprish client, when client change the window size. 28 type UserEvent struct { 29 theType UserEventType 30 userByte terminal.UserByte // Parser::UserByte 31 resize terminal.Resize // Parser::Resize 32 } 33 34 func NewUserEvent(userByte terminal.UserByte) (u UserEvent) { 35 u = UserEvent{} 36 37 u.theType = UserByteType 38 u.userByte = userByte 39 40 return u 41 } 42 43 func NewUserEventResize(resize terminal.Resize) (u UserEvent) { 44 u = UserEvent{} 45 46 u.theType = ResizeType 47 u.resize = resize 48 49 return u 50 } 51 52 // UserStream implements network.State[C any] interface 53 type UserStream struct { 54 actions []UserEvent 55 } 56 57 func (u *UserStream) String() string { 58 var output1 strings.Builder 59 var output2 strings.Builder 60 61 for _, v := range u.actions { 62 switch v.theType { 63 case UserByteType: 64 // output1.WriteRune(v.userByte.C) 65 output1.WriteString(string(v.userByte.Chs)) 66 case ResizeType: 67 output2.WriteString(fmt.Sprintf("(%d,%d),", v.resize.Width, v.resize.Height)) 68 } 69 } 70 71 return fmt.Sprintf("Keystroke:%q, Resize:%s, size=%d", output1.String(), output2.String(), len(u.actions)) 72 } 73 74 func (u *UserStream) PushBack(x []rune) { 75 userStroke := terminal.UserByte{Chs: x} 76 u.actions = append(u.actions, NewUserEvent(userStroke)) 77 } 78 79 func (u *UserStream) PushBackResize(width, height int) { 80 resize := terminal.Resize{Width: width, Height: height} 81 u.actions = append(u.actions, NewUserEventResize(resize)) 82 } 83 84 func (u *UserStream) Empty() bool { 85 return len(u.actions) == 0 86 } 87 88 func (u *UserStream) Size() int { 89 return len(u.actions) 90 } 91 92 func (u *UserStream) GetAction(i int) terminal.ActOn { 93 if 0 <= i && i < len(u.actions) { 94 switch u.actions[i].theType { 95 case UserByteType: 96 return u.actions[i].userByte 97 case ResizeType: 98 return u.actions[i].resize 99 } 100 } 101 102 return nil 103 } 104 105 // implements network.State[C any] interface 106 // Subtract() the prefix UserStream from current UserStream 107 func (u *UserStream) Subtract(prefix *UserStream) { 108 // fmt.Printf("#Subtract %q %p from %q %p\n", prefix, prefix, u, u) 109 // if we are subtracting ourself from ourself, just clear the deque 110 if u.Equal(prefix) { 111 u.actions = make([]UserEvent, 0) 112 return 113 } 114 115 for i := range prefix.actions { 116 // fmt.Printf("#Subtract compare %q[0] vs %q[%d]\n", u.actions[0], prefix.actions[i], i) 117 if len(u.actions) > 0 && reflect.DeepEqual(u.actions[0], prefix.actions[i]) { 118 // fmt.Printf("#Subtract equal %d %q\n", i, prefix.actions[i]) 119 u.actions = u.actions[1:] 120 } else { 121 // fmt.Printf("#Subtract save %d %q\n", i, prefix.actions[i]) 122 break 123 } 124 } 125 } 126 127 // implements network.State[C any] interface 128 // DiffFrom() exclude the existing UserEvent and return the difference. 129 func (u *UserStream) DiffFrom(existing *UserStream) string { 130 // skip the existing part 131 pos := 0 132 for i := range existing.actions { 133 if len(u.actions[pos:]) > 0 && reflect.DeepEqual(u.actions[pos], existing.actions[i]) { 134 pos++ 135 } else { 136 break 137 } 138 } 139 140 // create the UserMessage based on content in UserStream 141 um := pb.UserMessage{} 142 for _, ue := range u.actions[pos:] { 143 switch ue.theType { 144 case UserByteType: 145 idx := len(um.Instruction) - 1 // TODO the last one? 146 var buf bytes.Buffer 147 buf.WriteString(string(ue.userByte.Chs)) 148 keys := buf.Bytes() 149 150 if len(um.Instruction) > 0 && um.Instruction[idx].Keystroke != nil { 151 // append Keys for Keystroke 152 153 um.Instruction[idx].Keystroke.Keys = append(um.Instruction[idx].Keystroke.Keys, keys...) 154 } else { 155 // create a new Instruction for Keystroke 156 um.Instruction = make([]*pb.Instruction, 0) 157 158 inst := pb.Instruction{ 159 Keystroke: &pb.Keystroke{Keys: keys}, 160 } 161 um.Instruction = append(um.Instruction, &inst) 162 } 163 case ResizeType: 164 // create a new Instruction for ResizeMessage 165 inst := pb.Instruction{ 166 Resize: &pb.ResizeMessage{Width: int32(ue.resize.Width), Height: int32(ue.resize.Height)}, 167 } 168 um.Instruction = append(um.Instruction, &inst) 169 } 170 } 171 172 // get the wire-format encoding of UserMessage 173 output, _ := proto.Marshal(&um) 174 // if err != nil { 175 // panic(fmt.Sprintf("#DiffFrom marshal %s ", err)) 176 // } 177 178 return string(output) 179 } 180 181 // implements network.State[C any] interface 182 func (u *UserStream) InitDiff() string { 183 return "" 184 } 185 186 // implements network.State[C any] interface 187 // convert the UserMessage into a UserStream 188 func (u *UserStream) ApplyString(diff string) error { 189 // parse the wire-format encoding of UserMessage 190 input := pb.UserMessage{} 191 err := proto.Unmarshal([]byte(diff), &input) 192 if err != nil { 193 return err 194 } 195 196 // create the UserStream based on content of UserMessage 197 for i := range input.Instruction { 198 if input.Instruction[i].Keystroke != nil { 199 graphemes := uniseg.NewGraphemes(string(input.Instruction[i].Keystroke.Keys)) 200 201 for graphemes.Next() { 202 chs := graphemes.Runes() 203 u.actions = append(u.actions, NewUserEvent(terminal.UserByte{Chs: chs})) 204 } 205 } else if input.Instruction[i].Resize != nil { 206 w := input.Instruction[i].Resize 207 u.actions = append(u.actions, NewUserEventResize(terminal.Resize{Width: int(w.Width), Height: int(w.Height)})) 208 } 209 } 210 211 return nil 212 } 213 214 // implements network.State[C any] interface 215 func (u *UserStream) Equal(x *UserStream) bool { 216 if u == x || (len(u.actions) == 0 && len(x.actions) == 0) { 217 return true 218 } 219 return reflect.DeepEqual(u.actions, x.actions) 220 } 221 222 // implements network.State[C any] interface 223 func (u *UserStream) ResetInput() {} 224 func (u *UserStream) Reset() {} 225 func (u *UserStream) InitSize(nCols, nRows int) {} 226 227 // implements network.State[C any] interface 228 func (u *UserStream) Clone() *UserStream { 229 clone := UserStream{} 230 clone.actions = make([]UserEvent, len(u.actions)) 231 232 for i := range u.actions { // actions slice 233 if u.actions[i].theType == UserByteType { // clone UserByte 234 chs := make([]rune, len(u.actions[i].userByte.Chs)) 235 copy(chs, u.actions[i].userByte.Chs) 236 237 clone.actions[i].userByte = terminal.UserByte{} 238 clone.actions[i].userByte.Chs = chs 239 } else { 240 clone.actions[i] = u.actions[i] // clone Resize 241 } 242 } 243 244 return &clone 245 } 246 247 // for test purpose 248 func (u *UserStream) EqualTrace(x *UserStream) bool { 249 return u.Equal(x) 250 }