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