]> git.lizzy.rs Git - micro.git/blob - cmd/micro/eventhandler.go
Boss mode
[micro.git] / cmd / micro / eventhandler.go
1 package main
2
3 import (
4         "time"
5 )
6
7 const (
8         // Opposite and undoing events must have opposite values
9
10         // TextEventInsert repreasents an insertion event
11         TextEventInsert = 1
12         // TextEventRemove represents a deletion event
13         TextEventRemove = -1
14 )
15
16 // TextEvent holds data for a manipulation on some text that can be undone
17 type TextEvent struct {
18         c Cursor
19
20         eventType int
21         text      string
22         start     int
23         end       int
24         buf       *Buffer
25         time      time.Time
26 }
27
28 // ExecuteTextEvent runs a text event
29 func ExecuteTextEvent(t *TextEvent) {
30         if t.eventType == TextEventInsert {
31                 t.buf.Insert(t.start, t.text)
32         } else if t.eventType == TextEventRemove {
33                 t.text = t.buf.Remove(t.start, t.end)
34         }
35 }
36
37 // UndoTextEvent undoes a text event
38 func UndoTextEvent(t *TextEvent) {
39         t.eventType = -t.eventType
40         ExecuteTextEvent(t)
41 }
42
43 // EventHandler executes text manipulations and allows undoing and redoing
44 type EventHandler struct {
45         v    *View
46         undo *Stack
47         redo *Stack
48 }
49
50 // NewEventHandler returns a new EventHandler
51 func NewEventHandler(v *View) *EventHandler {
52         eh := new(EventHandler)
53         eh.undo = new(Stack)
54         eh.redo = new(Stack)
55         eh.v = v
56         return eh
57 }
58
59 // Insert creates an insert text event and executes it
60 func (eh *EventHandler) Insert(start int, text string) {
61         e := &TextEvent{
62                 c:         eh.v.cursor,
63                 eventType: TextEventInsert,
64                 text:      text,
65                 start:     start,
66                 end:       start + Count(text),
67                 buf:       eh.v.buf,
68                 time:      time.Now(),
69         }
70         eh.Execute(e)
71 }
72
73 // Remove creates a remove text event and executes it
74 func (eh *EventHandler) Remove(start, end int) {
75         e := &TextEvent{
76                 c:         eh.v.cursor,
77                 eventType: TextEventRemove,
78                 start:     start,
79                 end:       end,
80                 buf:       eh.v.buf,
81                 time:      time.Now(),
82         }
83         eh.Execute(e)
84 }
85
86 // Replace deletes from start to end and replaces it with the given string
87 func (eh *EventHandler) Replace(start, end int, replace string) {
88         eh.Remove(start, end)
89         eh.Insert(start, replace)
90 }
91
92 // Execute a textevent and add it to the undo stack
93 func (eh *EventHandler) Execute(t *TextEvent) {
94         if eh.redo.Len() > 0 {
95                 eh.redo = new(Stack)
96         }
97         eh.undo.Push(t)
98         ExecuteTextEvent(t)
99 }
100
101 // Undo the first event in the undo stack
102 func (eh *EventHandler) Undo() {
103         t := eh.undo.Peek()
104         if t == nil {
105                 return
106         }
107
108         te := t.(*TextEvent)
109         startTime := t.(*TextEvent).time.UnixNano() / int64(time.Millisecond)
110
111         eh.UndoOneEvent()
112
113         for {
114                 t = eh.undo.Peek()
115                 if t == nil {
116                         return
117                 }
118
119                 te = t.(*TextEvent)
120
121                 if startTime-(te.time.UnixNano()/int64(time.Millisecond)) > undoThreshold {
122                         return
123                 }
124
125                 eh.UndoOneEvent()
126         }
127 }
128
129 // UndoOneEvent undoes one event
130 func (eh *EventHandler) UndoOneEvent() {
131         // This event should be undone
132         // Pop it off the stack
133         t := eh.undo.Pop()
134         if t == nil {
135                 return
136         }
137
138         te := t.(*TextEvent)
139         // Undo it
140         // Modifies the text event
141         UndoTextEvent(te)
142
143         // Set the cursor in the right place
144         teCursor := te.c
145         te.c = eh.v.cursor
146         eh.v.cursor = teCursor
147
148         // Push it to the redo stack
149         eh.redo.Push(te)
150 }
151
152 // Redo the first event in the redo stack
153 func (eh *EventHandler) Redo() {
154         t := eh.redo.Peek()
155         if t == nil {
156                 return
157         }
158
159         te := t.(*TextEvent)
160         startTime := t.(*TextEvent).time.UnixNano() / int64(time.Millisecond)
161
162         eh.RedoOneEvent()
163
164         for {
165                 t = eh.redo.Peek()
166                 if t == nil {
167                         return
168                 }
169
170                 te = t.(*TextEvent)
171
172                 if (te.time.UnixNano()/int64(time.Millisecond))-startTime > undoThreshold {
173                         return
174                 }
175
176                 eh.RedoOneEvent()
177         }
178 }
179
180 // RedoOneEvent redoes one event
181 func (eh *EventHandler) RedoOneEvent() {
182         t := eh.redo.Pop()
183         if t == nil {
184                 return
185         }
186
187         te := t.(*TextEvent)
188         // Modifies the text event
189         UndoTextEvent(te)
190
191         teCursor := te.c
192         te.c = eh.v.cursor
193         eh.v.cursor = teCursor
194
195         eh.undo.Push(te)
196 }