]> git.lizzy.rs Git - micro.git/blob - internal/action/termpane.go
bbb1e17b7cd10fc3dfc7453349a0102c7a8b5fab
[micro.git] / internal / action / termpane.go
1 package action
2
3 import (
4         "errors"
5         "runtime"
6
7         "github.com/zyedidia/micro/v2/internal/clipboard"
8         "github.com/zyedidia/micro/v2/internal/config"
9         "github.com/zyedidia/micro/v2/internal/display"
10         "github.com/zyedidia/micro/v2/internal/screen"
11         "github.com/zyedidia/micro/v2/internal/shell"
12         "github.com/zyedidia/tcell/v2"
13         "github.com/zyedidia/terminal"
14 )
15
16 type TermKeyAction func(*TermPane)
17
18 var TermBindings *KeyTree
19
20 func init() {
21         TermBindings = NewKeyTree()
22 }
23
24 func TermKeyActionGeneral(a TermKeyAction) PaneKeyAction {
25         return func(p Pane) bool {
26                 a(p.(*TermPane))
27                 return true
28         }
29 }
30
31 func TermMapEvent(k Event, action string) {
32         config.Bindings["terminal"][k.Name()] = action
33
34         switch e := k.(type) {
35         case KeyEvent, KeySequenceEvent, RawEvent:
36                 termMapKey(e, action)
37         case MouseEvent:
38                 termMapMouse(e, action)
39         }
40 }
41
42 func termMapKey(k Event, action string) {
43         if f, ok := TermKeyActions[action]; ok {
44                 TermBindings.RegisterKeyBinding(k, TermKeyActionGeneral(f))
45         }
46 }
47
48 func termMapMouse(k MouseEvent, action string) {
49         // TODO: map mouse
50         termMapKey(k, action)
51 }
52
53 type TermPane struct {
54         *shell.Terminal
55         display.Window
56
57         mouseReleased bool
58         id            uint64
59         tab           *Tab
60 }
61
62 func NewTermPane(x, y, w, h int, t *shell.Terminal, id uint64, tab *Tab) (*TermPane, error) {
63         if !TermEmuSupported {
64                 return nil, errors.New("Terminal emulator is not supported on this system")
65         }
66
67         th := new(TermPane)
68         th.Terminal = t
69         th.id = id
70         th.mouseReleased = true
71         th.Window = display.NewTermWindow(x, y, w, h, t)
72         th.tab = tab
73         return th, nil
74 }
75
76 func (t *TermPane) ID() uint64 {
77         return t.id
78 }
79
80 func (t *TermPane) SetID(i uint64) {
81         t.id = i
82 }
83
84 func (t *TermPane) SetTab(tab *Tab) {
85         t.tab = tab
86 }
87
88 func (t *TermPane) Tab() *Tab {
89         return t.tab
90 }
91
92 func (t *TermPane) Close() {}
93
94 // Quit closes this termpane
95 func (t *TermPane) Quit() {
96         t.Close()
97         if len(MainTab().Panes) > 1 {
98                 t.Unsplit()
99         } else if len(Tabs.List) > 1 {
100                 Tabs.RemoveTab(t.id)
101         } else {
102                 screen.Screen.Fini()
103                 InfoBar.Close()
104                 runtime.Goexit()
105         }
106 }
107
108 // Unsplit removes this split
109 func (t *TermPane) Unsplit() {
110         n := MainTab().GetNode(t.id)
111         n.Unsplit()
112
113         MainTab().RemovePane(MainTab().GetPane(t.id))
114         MainTab().Resize()
115         MainTab().SetActive(len(MainTab().Panes) - 1)
116 }
117
118 // HandleEvent handles a tcell event by forwarding it to the terminal emulator
119 // If the event is a mouse event and the program running in the emulator
120 // does not have mouse support, the emulator will support selections and
121 // copy-paste
122 func (t *TermPane) HandleEvent(event tcell.Event) {
123         if e, ok := event.(*tcell.EventKey); ok {
124                 ke := KeyEvent{
125                         code: e.Key(),
126                         mod:  metaToAlt(e.Modifiers()),
127                         r:    e.Rune(),
128                 }
129                 action, more := TermBindings.NextEvent(ke, nil)
130
131                 if !more {
132                         if action != nil {
133                                 action(t)
134                                 TermBindings.ResetEvents()
135                                 return
136                         }
137                         TermBindings.ResetEvents()
138                 }
139
140                 if more {
141                         return
142                 }
143
144                 if t.Status == shell.TTDone {
145                         switch e.Key() {
146                         case tcell.KeyEscape, tcell.KeyCtrlQ, tcell.KeyEnter:
147                                 t.Close()
148                                 t.Quit()
149                         default:
150                         }
151                 }
152                 if e.Key() == tcell.KeyCtrlC && t.HasSelection() {
153                         clipboard.Write(t.GetSelection(t.GetView().Width), clipboard.ClipboardReg)
154                         InfoBar.Message("Copied selection to clipboard")
155                 } else if t.Status != shell.TTDone {
156                         t.WriteString(event.EscSeq())
157                 }
158         } else if _, ok := event.(*tcell.EventPaste); ok {
159                 if t.Status != shell.TTDone {
160                         t.WriteString(event.EscSeq())
161                 }
162         } else if e, ok := event.(*tcell.EventMouse); e != nil && (!ok || t.State.Mode(terminal.ModeMouseMask)) {
163                 // t.WriteString(event.EscSeq())
164         } else if e != nil {
165                 x, y := e.Position()
166                 v := t.GetView()
167                 x -= v.X
168                 y -= v.Y
169
170                 if e.Buttons() == tcell.Button1 {
171                         if !t.mouseReleased {
172                                 // drag
173                                 t.Selection[1].X = x
174                                 t.Selection[1].Y = y
175                         } else {
176                                 t.Selection[0].X = x
177                                 t.Selection[0].Y = y
178                                 t.Selection[1].X = x
179                                 t.Selection[1].Y = y
180                         }
181
182                         t.mouseReleased = false
183                 } else if e.Buttons() == tcell.ButtonNone {
184                         if !t.mouseReleased {
185                                 t.Selection[1].X = x
186                                 t.Selection[1].Y = y
187                         }
188                         t.mouseReleased = true
189                 }
190         }
191
192         if t.Status == shell.TTClose {
193                 t.Quit()
194         }
195 }
196
197 // Exit closes the termpane
198 func (t *TermPane) Exit() {
199         t.Terminal.Close()
200         t.Quit()
201 }
202
203 // CommandMode opens the termpane's command mode
204 func (t *TermPane) CommandMode() {
205         InfoBar.Prompt("> ", "", "TerminalCommand", nil, func(resp string, canceled bool) {
206                 if !canceled {
207                         t.HandleCommand(resp)
208                 }
209         })
210 }
211
212 // NextSplit moves to the next split
213 func (t *TermPane) NextSplit() {
214         a := t.tab.active
215         if a < len(t.tab.Panes)-1 {
216                 a++
217         } else {
218                 a = 0
219         }
220
221         t.tab.SetActive(a)
222 }
223
224 // HandleCommand handles a command for the term pane
225 func (t *TermPane) HandleCommand(input string) {
226         InfoBar.Error("Commands are unsupported in term for now")
227 }
228
229 // TermKeyActions contains the list of all possible key actions the termpane could execute
230 var TermKeyActions = map[string]TermKeyAction{
231         "Exit":        (*TermPane).Exit,
232         "CommandMode": (*TermPane).CommandMode,
233         "NextSplit":   (*TermPane).NextSplit,
234 }