]> git.lizzy.rs Git - micro.git/blob - cmd/micro/bindings.go
Use the new and updated version of tcell
[micro.git] / cmd / micro / bindings.go
1 package main
2
3 import (
4         "encoding/json"
5         "io/ioutil"
6         "os"
7         "strconv"
8         "strings"
9         "time"
10
11         "github.com/mitchellh/go-homedir"
12         "github.com/zyedidia/clipboard"
13         "github.com/zyedidia/tcell"
14 )
15
16 var bindings map[Key]func(*View) bool
17
18 // The Key struct holds the data for a keypress (keycode + modifiers)
19 type Key struct {
20         keyCode   tcell.Key
21         modifiers tcell.ModMask
22 }
23
24 // InitBindings initializes the keybindings for micro
25 func InitBindings() {
26         bindings = make(map[Key]func(*View) bool)
27
28         actions := map[string]func(*View) bool{
29                 "CursorUp":            (*View).CursorUp,
30                 "CursorDown":          (*View).CursorDown,
31                 "CursorLeft":          (*View).CursorLeft,
32                 "CursorRight":         (*View).CursorRight,
33                 "CursorStart":         (*View).CursorStart,
34                 "CursorEnd":           (*View).CursorEnd,
35                 "SelectToStart":       (*View).SelectToStart,
36                 "SelectToEnd":         (*View).SelectToEnd,
37                 "SelectUp":            (*View).SelectUp,
38                 "SelectDown":          (*View).SelectDown,
39                 "SelectLeft":          (*View).SelectLeft,
40                 "SelectRight":         (*View).SelectRight,
41                 "WordRight":           (*View).WordRight,
42                 "WordLeft":            (*View).WordLeft,
43                 "SelectWordRight":     (*View).SelectWordRight,
44                 "SelectWordLeft":      (*View).SelectWordLeft,
45                 "SelectToStartOfLine": (*View).SelectToStartOfLine,
46                 "SelectToEndOfLine":   (*View).SelectToEndOfLine,
47                 "InsertEnter":         (*View).InsertEnter,
48                 "InsertSpace":         (*View).InsertSpace,
49                 "Backspace":           (*View).Backspace,
50                 "Delete":              (*View).Delete,
51                 "InsertTab":           (*View).InsertTab,
52                 "Save":                (*View).Save,
53                 "Find":                (*View).Find,
54                 "FindNext":            (*View).FindNext,
55                 "FindPrevious":        (*View).FindPrevious,
56                 "Undo":                (*View).Undo,
57                 "Redo":                (*View).Redo,
58                 "Copy":                (*View).Copy,
59                 "Cut":                 (*View).Cut,
60                 "CutLine":             (*View).CutLine,
61                 "Paste":               (*View).Paste,
62                 "SelectAll":           (*View).SelectAll,
63                 "OpenFile":            (*View).OpenFile,
64                 "Start":               (*View).Start,
65                 "End":                 (*View).End,
66                 "PageUp":              (*View).PageUp,
67                 "PageDown":            (*View).PageDown,
68                 "HalfPageUp":          (*View).HalfPageUp,
69                 "HalfPageDown":        (*View).HalfPageDown,
70                 "StartOfLine":         (*View).StartOfLine,
71                 "EndOfLine":           (*View).EndOfLine,
72                 "ToggleRuler":         (*View).ToggleRuler,
73                 "JumpLine":            (*View).JumpLine,
74         }
75
76         keys := map[string]Key{
77                 "Up":             Key{tcell.KeyUp, tcell.ModNone},
78                 "Down":           Key{tcell.KeyDown, tcell.ModNone},
79                 "Right":          Key{tcell.KeyRight, tcell.ModNone},
80                 "Left":           Key{tcell.KeyLeft, tcell.ModNone},
81                 "AltUp":          Key{tcell.KeyUp, tcell.ModAlt},
82                 "AltDown":        Key{tcell.KeyDown, tcell.ModAlt},
83                 "AltLeft":        Key{tcell.KeyLeft, tcell.ModAlt},
84                 "AltRight":       Key{tcell.KeyRight, tcell.ModAlt},
85                 "CtrlUp":         Key{tcell.KeyUp, tcell.ModCtrl},
86                 "CtrlDown":       Key{tcell.KeyDown, tcell.ModCtrl},
87                 "CtrlLeft":       Key{tcell.KeyLeft, tcell.ModCtrl},
88                 "CtrlRight":      Key{tcell.KeyRight, tcell.ModCtrl},
89                 "ShiftUp":        Key{tcell.KeyUp, tcell.ModShift},
90                 "ShiftDown":      Key{tcell.KeyDown, tcell.ModShift},
91                 "ShiftLeft":      Key{tcell.KeyLeft, tcell.ModShift},
92                 "ShiftRight":     Key{tcell.KeyRight, tcell.ModShift},
93                 "AltShiftUp":     Key{tcell.KeyUp, tcell.ModShift | tcell.ModAlt},
94                 "AltShiftDown":   Key{tcell.KeyDown, tcell.ModShift | tcell.ModAlt},
95                 "AltShiftLeft":   Key{tcell.KeyLeft, tcell.ModShift | tcell.ModAlt},
96                 "AltShiftRight":  Key{tcell.KeyRight, tcell.ModShift | tcell.ModAlt},
97                 "CtrlShiftUp":    Key{tcell.KeyUp, tcell.ModShift | tcell.ModCtrl},
98                 "CtrlShiftDown":  Key{tcell.KeyDown, tcell.ModShift | tcell.ModCtrl},
99                 "CtrlShiftLeft":  Key{tcell.KeyLeft, tcell.ModShift | tcell.ModCtrl},
100                 "CtrlShiftRight": Key{tcell.KeyRight, tcell.ModShift | tcell.ModCtrl},
101                 "UpLeft":         Key{tcell.KeyUpLeft, tcell.ModNone},
102                 "UpRight":        Key{tcell.KeyUpRight, tcell.ModNone},
103                 "DownLeft":       Key{tcell.KeyDownLeft, tcell.ModNone},
104                 "DownRight":      Key{tcell.KeyDownRight, tcell.ModNone},
105                 "Center":         Key{tcell.KeyCenter, tcell.ModNone},
106                 "PgUp":           Key{tcell.KeyPgUp, tcell.ModNone},
107                 "PgDn":           Key{tcell.KeyPgDn, tcell.ModNone},
108                 "Home":           Key{tcell.KeyHome, tcell.ModNone},
109                 "End":            Key{tcell.KeyEnd, tcell.ModNone},
110                 "Insert":         Key{tcell.KeyInsert, tcell.ModNone},
111                 "Delete":         Key{tcell.KeyDelete, tcell.ModNone},
112                 "Help":           Key{tcell.KeyHelp, tcell.ModNone},
113                 "Exit":           Key{tcell.KeyExit, tcell.ModNone},
114                 "Clear":          Key{tcell.KeyClear, tcell.ModNone},
115                 "Cancel":         Key{tcell.KeyCancel, tcell.ModNone},
116                 "Print":          Key{tcell.KeyPrint, tcell.ModNone},
117                 "Pause":          Key{tcell.KeyPause, tcell.ModNone},
118                 "Backtab":        Key{tcell.KeyBacktab, tcell.ModNone},
119                 "F1":             Key{tcell.KeyF1, tcell.ModNone},
120                 "F2":             Key{tcell.KeyF2, tcell.ModNone},
121                 "F3":             Key{tcell.KeyF3, tcell.ModNone},
122                 "F4":             Key{tcell.KeyF4, tcell.ModNone},
123                 "F5":             Key{tcell.KeyF5, tcell.ModNone},
124                 "F6":             Key{tcell.KeyF6, tcell.ModNone},
125                 "F7":             Key{tcell.KeyF7, tcell.ModNone},
126                 "F8":             Key{tcell.KeyF8, tcell.ModNone},
127                 "F9":             Key{tcell.KeyF9, tcell.ModNone},
128                 "F10":            Key{tcell.KeyF10, tcell.ModNone},
129                 "F11":            Key{tcell.KeyF11, tcell.ModNone},
130                 "F12":            Key{tcell.KeyF12, tcell.ModNone},
131                 "F13":            Key{tcell.KeyF13, tcell.ModNone},
132                 "F14":            Key{tcell.KeyF14, tcell.ModNone},
133                 "F15":            Key{tcell.KeyF15, tcell.ModNone},
134                 "F16":            Key{tcell.KeyF16, tcell.ModNone},
135                 "F17":            Key{tcell.KeyF17, tcell.ModNone},
136                 "F18":            Key{tcell.KeyF18, tcell.ModNone},
137                 "F19":            Key{tcell.KeyF19, tcell.ModNone},
138                 "F20":            Key{tcell.KeyF20, tcell.ModNone},
139                 "F21":            Key{tcell.KeyF21, tcell.ModNone},
140                 "F22":            Key{tcell.KeyF22, tcell.ModNone},
141                 "F23":            Key{tcell.KeyF23, tcell.ModNone},
142                 "F24":            Key{tcell.KeyF24, tcell.ModNone},
143                 "F25":            Key{tcell.KeyF25, tcell.ModNone},
144                 "F26":            Key{tcell.KeyF26, tcell.ModNone},
145                 "F27":            Key{tcell.KeyF27, tcell.ModNone},
146                 "F28":            Key{tcell.KeyF28, tcell.ModNone},
147                 "F29":            Key{tcell.KeyF29, tcell.ModNone},
148                 "F30":            Key{tcell.KeyF30, tcell.ModNone},
149                 "F31":            Key{tcell.KeyF31, tcell.ModNone},
150                 "F32":            Key{tcell.KeyF32, tcell.ModNone},
151                 "F33":            Key{tcell.KeyF33, tcell.ModNone},
152                 "F34":            Key{tcell.KeyF34, tcell.ModNone},
153                 "F35":            Key{tcell.KeyF35, tcell.ModNone},
154                 "F36":            Key{tcell.KeyF36, tcell.ModNone},
155                 "F37":            Key{tcell.KeyF37, tcell.ModNone},
156                 "F38":            Key{tcell.KeyF38, tcell.ModNone},
157                 "F39":            Key{tcell.KeyF39, tcell.ModNone},
158                 "F40":            Key{tcell.KeyF40, tcell.ModNone},
159                 "F41":            Key{tcell.KeyF41, tcell.ModNone},
160                 "F42":            Key{tcell.KeyF42, tcell.ModNone},
161                 "F43":            Key{tcell.KeyF43, tcell.ModNone},
162                 "F44":            Key{tcell.KeyF44, tcell.ModNone},
163                 "F45":            Key{tcell.KeyF45, tcell.ModNone},
164                 "F46":            Key{tcell.KeyF46, tcell.ModNone},
165                 "F47":            Key{tcell.KeyF47, tcell.ModNone},
166                 "F48":            Key{tcell.KeyF48, tcell.ModNone},
167                 "F49":            Key{tcell.KeyF49, tcell.ModNone},
168                 "F50":            Key{tcell.KeyF50, tcell.ModNone},
169                 "F51":            Key{tcell.KeyF51, tcell.ModNone},
170                 "F52":            Key{tcell.KeyF52, tcell.ModNone},
171                 "F53":            Key{tcell.KeyF53, tcell.ModNone},
172                 "F54":            Key{tcell.KeyF54, tcell.ModNone},
173                 "F55":            Key{tcell.KeyF55, tcell.ModNone},
174                 "F56":            Key{tcell.KeyF56, tcell.ModNone},
175                 "F57":            Key{tcell.KeyF57, tcell.ModNone},
176                 "F58":            Key{tcell.KeyF58, tcell.ModNone},
177                 "F59":            Key{tcell.KeyF59, tcell.ModNone},
178                 "F60":            Key{tcell.KeyF60, tcell.ModNone},
179                 "F61":            Key{tcell.KeyF61, tcell.ModNone},
180                 "F62":            Key{tcell.KeyF62, tcell.ModNone},
181                 "F63":            Key{tcell.KeyF63, tcell.ModNone},
182                 "F64":            Key{tcell.KeyF64, tcell.ModNone},
183                 "CtrlSpace":      Key{tcell.KeyCtrlSpace, tcell.ModNone},
184                 "CtrlA":          Key{tcell.KeyCtrlA, tcell.ModNone},
185                 "CtrlB":          Key{tcell.KeyCtrlB, tcell.ModNone},
186                 "CtrlC":          Key{tcell.KeyCtrlC, tcell.ModNone},
187                 "CtrlD":          Key{tcell.KeyCtrlD, tcell.ModNone},
188                 "CtrlE":          Key{tcell.KeyCtrlE, tcell.ModNone},
189                 "CtrlF":          Key{tcell.KeyCtrlF, tcell.ModNone},
190                 "CtrlG":          Key{tcell.KeyCtrlG, tcell.ModNone},
191                 "CtrlH":          Key{tcell.KeyCtrlH, tcell.ModNone},
192                 "CtrlI":          Key{tcell.KeyCtrlI, tcell.ModNone},
193                 "CtrlJ":          Key{tcell.KeyCtrlJ, tcell.ModNone},
194                 "CtrlK":          Key{tcell.KeyCtrlK, tcell.ModNone},
195                 "CtrlL":          Key{tcell.KeyCtrlL, tcell.ModNone},
196                 "CtrlM":          Key{tcell.KeyCtrlM, tcell.ModNone},
197                 "CtrlN":          Key{tcell.KeyCtrlN, tcell.ModNone},
198                 "CtrlO":          Key{tcell.KeyCtrlO, tcell.ModNone},
199                 "CtrlP":          Key{tcell.KeyCtrlP, tcell.ModNone},
200                 "CtrlQ":          Key{tcell.KeyCtrlQ, tcell.ModNone},
201                 "CtrlR":          Key{tcell.KeyCtrlR, tcell.ModNone},
202                 "CtrlS":          Key{tcell.KeyCtrlS, tcell.ModNone},
203                 "CtrlT":          Key{tcell.KeyCtrlT, tcell.ModNone},
204                 "CtrlU":          Key{tcell.KeyCtrlU, tcell.ModNone},
205                 "CtrlV":          Key{tcell.KeyCtrlV, tcell.ModNone},
206                 "CtrlW":          Key{tcell.KeyCtrlW, tcell.ModNone},
207                 "CtrlX":          Key{tcell.KeyCtrlX, tcell.ModNone},
208                 "CtrlY":          Key{tcell.KeyCtrlY, tcell.ModNone},
209                 "CtrlZ":          Key{tcell.KeyCtrlZ, tcell.ModNone},
210                 "CtrlLeftSq":     Key{tcell.KeyCtrlLeftSq, tcell.ModNone},
211                 "CtrlBackslash":  Key{tcell.KeyCtrlBackslash, tcell.ModNone},
212                 "CtrlRightSq":    Key{tcell.KeyCtrlRightSq, tcell.ModNone},
213                 "CtrlCarat":      Key{tcell.KeyCtrlCarat, tcell.ModNone},
214                 "CtrlUnderscore": Key{tcell.KeyCtrlUnderscore, tcell.ModNone},
215                 "Backspace":      Key{tcell.KeyBackspace, tcell.ModNone},
216                 "Tab":            Key{tcell.KeyTab, tcell.ModNone},
217                 "Esc":            Key{tcell.KeyEsc, tcell.ModNone},
218                 "Escape":         Key{tcell.KeyEscape, tcell.ModNone},
219                 "Enter":          Key{tcell.KeyEnter, tcell.ModNone},
220                 "Backspace2":     Key{tcell.KeyBackspace2, tcell.ModNone},
221         }
222
223         var parsed map[string]string
224         defaults := DefaultBindings()
225
226         filename := configDir + "/bindings.json"
227         if _, e := os.Stat(filename); e == nil {
228                 input, err := ioutil.ReadFile(filename)
229                 if err != nil {
230                         TermMessage("Error reading bindings.json file: " + err.Error())
231                         return
232                 }
233
234                 err = json.Unmarshal(input, &parsed)
235                 if err != nil {
236                         TermMessage("Error reading bindings.json:", err.Error())
237                 }
238         }
239
240         for k, v := range defaults {
241                 bindings[keys[k]] = actions[v]
242         }
243         for k, v := range parsed {
244                 bindings[keys[k]] = actions[v]
245         }
246 }
247
248 // DefaultBindings returns a map containing micro's default keybindings
249 func DefaultBindings() map[string]string {
250         return map[string]string{
251                 "Up":             "CursorUp",
252                 "Down":           "CursorDown",
253                 "Right":          "CursorRight",
254                 "Left":           "CursorLeft",
255                 "ShiftUp":        "SelectUp",
256                 "ShiftDown":      "SelectDown",
257                 "ShiftLeft":      "SelectLeft",
258                 "ShiftRight":     "SelectRight",
259                 "AltLeft":        "WordLeft",
260                 "AltRight":       "WordRight",
261                 "AltShiftRight":  "SelectWordRight",
262                 "AltShiftLeft":   "SelectWordLeft",
263                 "CtrlLeft":       "StartOfLine",
264                 "CtrlRight":      "EndOfLine",
265                 "CtrlShiftLeft":  "SelectToStartOfLine",
266                 "CtrlShiftRight": "SelectToEndOfLine",
267                 "CtrlUp":         "CursorStart",
268                 "CtrlDown":       "CursorEnd",
269                 "CtrlShiftUp":    "SelectToStart",
270                 "CtrlShiftDown":  "SelectToEnd",
271                 "Enter":          "InsertEnter",
272                 "Space":          "InsertSpace",
273                 "Backspace":      "Backspace",
274                 "Backspace2":     "Backspace",
275                 "Tab":            "InsertTab",
276                 "CtrlO":          "OpenFile",
277                 "CtrlS":          "Save",
278                 "CtrlF":          "Find",
279                 "CtrlN":          "FindNext",
280                 "CtrlP":          "FindPrevious",
281                 "CtrlZ":          "Undo",
282                 "CtrlY":          "Redo",
283                 "CtrlC":          "Copy",
284                 "CtrlX":          "Cut",
285                 "CtrlK":          "CutLine",
286                 "CtrlV":          "Paste",
287                 "CtrlA":          "SelectAll",
288                 "Home":           "Start",
289                 "End":            "End",
290                 "PgUp":           "PageUp",
291                 "PgDn":           "PageDown",
292                 "CtrlU":          "HalfPageUp",
293                 "CtrlD":          "HalfPageDown",
294                 "CtrlR":          "ToggleRuler",
295                 "CtrlL":          "JumpLine",
296                 "Delete":         "Delete",
297         }
298 }
299
300 // CursorUp moves the cursor up
301 func (v *View) CursorUp() bool {
302         if v.Cursor.HasSelection() {
303                 v.Cursor.SetLoc(v.Cursor.curSelection[0])
304                 v.Cursor.ResetSelection()
305         }
306         v.Cursor.Up()
307         return true
308 }
309
310 // CursorDown moves the cursor down
311 func (v *View) CursorDown() bool {
312         if v.Cursor.HasSelection() {
313                 v.Cursor.SetLoc(v.Cursor.curSelection[1])
314                 v.Cursor.ResetSelection()
315         }
316         v.Cursor.Down()
317         return true
318 }
319
320 // CursorLeft moves the cursor left
321 func (v *View) CursorLeft() bool {
322         if v.Cursor.HasSelection() {
323                 v.Cursor.SetLoc(v.Cursor.curSelection[0])
324                 v.Cursor.ResetSelection()
325         } else {
326                 v.Cursor.Left()
327         }
328         return true
329 }
330
331 // CursorRight moves the cursor right
332 func (v *View) CursorRight() bool {
333         if v.Cursor.HasSelection() {
334                 v.Cursor.SetLoc(v.Cursor.curSelection[1] - 1)
335                 v.Cursor.ResetSelection()
336         } else {
337                 v.Cursor.Right()
338         }
339         return true
340 }
341
342 // WordRight moves the cursor one word to the right
343 func (v *View) WordRight() bool {
344         v.Cursor.WordRight()
345         return true
346 }
347
348 // WordLeft moves the cursor one word to the left
349 func (v *View) WordLeft() bool {
350         v.Cursor.WordLeft()
351         return true
352 }
353
354 // SelectUp selects up one line
355 func (v *View) SelectUp() bool {
356         loc := v.Cursor.Loc()
357         if !v.Cursor.HasSelection() {
358                 v.Cursor.origSelection[0] = loc
359         }
360         v.Cursor.Up()
361         v.Cursor.SelectTo(v.Cursor.Loc())
362         return true
363 }
364
365 // SelectDown selects down one line
366 func (v *View) SelectDown() bool {
367         loc := v.Cursor.Loc()
368         if !v.Cursor.HasSelection() {
369                 v.Cursor.origSelection[0] = loc
370         }
371         v.Cursor.Down()
372         v.Cursor.SelectTo(v.Cursor.Loc())
373         return true
374 }
375
376 // SelectLeft selects the character to the left of the cursor
377 func (v *View) SelectLeft() bool {
378         loc := v.Cursor.Loc()
379         count := v.Buf.Len() - 1
380         if loc > count {
381                 loc = count
382         }
383         if !v.Cursor.HasSelection() {
384                 v.Cursor.origSelection[0] = loc
385         }
386         v.Cursor.Left()
387         v.Cursor.SelectTo(v.Cursor.Loc())
388         return true
389 }
390
391 // SelectRight selects the character to the right of the cursor
392 func (v *View) SelectRight() bool {
393         loc := v.Cursor.Loc()
394         count := v.Buf.Len() - 1
395         if loc > count {
396                 loc = count
397         }
398         if !v.Cursor.HasSelection() {
399                 v.Cursor.origSelection[0] = loc
400         }
401         v.Cursor.Right()
402         v.Cursor.SelectTo(v.Cursor.Loc())
403         return true
404 }
405
406 // SelectWordRight selects the word to the right of the cursor
407 func (v *View) SelectWordRight() bool {
408         loc := v.Cursor.Loc()
409         if !v.Cursor.HasSelection() {
410                 v.Cursor.origSelection[0] = loc
411         }
412         v.Cursor.WordRight()
413         v.Cursor.SelectTo(v.Cursor.Loc())
414         return true
415 }
416
417 // SelectWordLeft selects the word to the left of the cursor
418 func (v *View) SelectWordLeft() bool {
419         loc := v.Cursor.Loc()
420         if !v.Cursor.HasSelection() {
421                 v.Cursor.origSelection[0] = loc
422         }
423         v.Cursor.WordLeft()
424         v.Cursor.SelectTo(v.Cursor.Loc())
425         return true
426 }
427
428 // StartOfLine moves the cursor to the start of the line
429 func (v *View) StartOfLine() bool {
430         v.Cursor.Start()
431         return true
432 }
433
434 // EndOfLine moves the cursor to the end of the line
435 func (v *View) EndOfLine() bool {
436         v.Cursor.End()
437         return true
438 }
439
440 // SelectToStartOfLine selects to the start of the current line
441 func (v *View) SelectToStartOfLine() bool {
442         loc := v.Cursor.Loc()
443         if !v.Cursor.HasSelection() {
444                 v.Cursor.origSelection[0] = loc
445         }
446         v.Cursor.Start()
447         v.Cursor.SelectTo(v.Cursor.Loc())
448         return true
449 }
450
451 // SelectToEndOfLine selects to the end of the current line
452 func (v *View) SelectToEndOfLine() bool {
453         loc := v.Cursor.Loc()
454         if !v.Cursor.HasSelection() {
455                 v.Cursor.origSelection[0] = loc
456         }
457         v.Cursor.End()
458         v.Cursor.SelectTo(v.Cursor.Loc())
459         return true
460 }
461
462 // CursorStart moves the cursor to the start of the buffer
463 func (v *View) CursorStart() bool {
464         v.Cursor.x = 0
465         v.Cursor.y = 0
466         return true
467 }
468
469 // CursorEnd moves the cursor to the end of the buffer
470 func (v *View) CursorEnd() bool {
471         v.Cursor.SetLoc(v.Buf.Len())
472         return true
473 }
474
475 // SelectToStart selects the text from the cursor to the start of the buffer
476 func (v *View) SelectToStart() bool {
477         loc := v.Cursor.Loc()
478         if !v.Cursor.HasSelection() {
479                 v.Cursor.origSelection[0] = loc
480         }
481         v.CursorStart()
482         v.Cursor.SelectTo(0)
483         return true
484 }
485
486 // SelectToEnd selects the text from the cursor to the end of the buffer
487 func (v *View) SelectToEnd() bool {
488         loc := v.Cursor.Loc()
489         if !v.Cursor.HasSelection() {
490                 v.Cursor.origSelection[0] = loc
491         }
492         v.CursorEnd()
493         v.Cursor.SelectTo(v.Buf.Len())
494         return true
495 }
496
497 // InsertSpace inserts a space
498 func (v *View) InsertSpace() bool {
499         // Insert a space
500         if v.Cursor.HasSelection() {
501                 v.Cursor.DeleteSelection()
502                 v.Cursor.ResetSelection()
503         }
504         v.eh.Insert(v.Cursor.Loc(), " ")
505         v.Cursor.Right()
506         return true
507 }
508
509 // InsertEnter inserts a newline plus possible some whitespace if autoindent is on
510 func (v *View) InsertEnter() bool {
511         // Insert a newline
512         if v.Cursor.HasSelection() {
513                 v.Cursor.DeleteSelection()
514                 v.Cursor.ResetSelection()
515         }
516
517         v.eh.Insert(v.Cursor.Loc(), "\n")
518         ws := GetLeadingWhitespace(v.Buf.Lines[v.Cursor.y])
519         v.Cursor.Right()
520
521         if settings["autoindent"].(bool) {
522                 v.eh.Insert(v.Cursor.Loc(), ws)
523                 for i := 0; i < len(ws); i++ {
524                         v.Cursor.Right()
525                 }
526         }
527         v.Cursor.lastVisualX = v.Cursor.GetVisualX()
528         return true
529 }
530
531 // Backspace deletes the previous character
532 func (v *View) Backspace() bool {
533         // Delete a character
534         if v.Cursor.HasSelection() {
535                 v.Cursor.DeleteSelection()
536                 v.Cursor.ResetSelection()
537         } else if v.Cursor.Loc() > 0 {
538                 // We have to do something a bit hacky here because we want to
539                 // delete the line by first moving left and then deleting backwards
540                 // but the undo redo would place the cursor in the wrong place
541                 // So instead we move left, save the position, move back, delete
542                 // and restore the position
543
544                 // If the user is using spaces instead of tabs and they are deleting
545                 // whitespace at the start of the line, we should delete as if its a
546                 // tab (tabSize number of spaces)
547                 lineStart := v.Buf.Lines[v.Cursor.y][:v.Cursor.x]
548                 tabSize := int(settings["tabsize"].(float64))
549                 if settings["tabsToSpaces"].(bool) && IsSpaces(lineStart) && len(lineStart) != 0 && len(lineStart)%tabSize == 0 {
550                         loc := v.Cursor.Loc()
551                         v.Cursor.SetLoc(loc - tabSize)
552                         cx, cy := v.Cursor.x, v.Cursor.y
553                         v.Cursor.SetLoc(loc)
554                         v.eh.Remove(loc-tabSize, loc)
555                         v.Cursor.x, v.Cursor.y = cx, cy
556                 } else {
557                         v.Cursor.Left()
558                         cx, cy := v.Cursor.x, v.Cursor.y
559                         v.Cursor.Right()
560                         loc := v.Cursor.Loc()
561                         v.eh.Remove(loc-1, loc)
562                         v.Cursor.x, v.Cursor.y = cx, cy
563                 }
564         }
565         v.Cursor.lastVisualX = v.Cursor.GetVisualX()
566         return true
567 }
568
569 // Delete deletes the next character
570 func (v *View) Delete() bool {
571         if v.Cursor.HasSelection() {
572                 v.Cursor.DeleteSelection()
573                 v.Cursor.ResetSelection()
574         } else {
575                 loc := v.Cursor.Loc()
576                 if loc < v.Buf.Len() {
577                         v.eh.Remove(loc, loc+1)
578                 }
579         }
580         return true
581 }
582
583 // InsertTab inserts a tab or spaces
584 func (v *View) InsertTab() bool {
585         // Insert a tab
586         if v.Cursor.HasSelection() {
587                 v.Cursor.DeleteSelection()
588                 v.Cursor.ResetSelection()
589         }
590         if settings["tabsToSpaces"].(bool) {
591                 tabSize := int(settings["tabsize"].(float64))
592                 v.eh.Insert(v.Cursor.Loc(), Spaces(tabSize))
593                 for i := 0; i < tabSize; i++ {
594                         v.Cursor.Right()
595                 }
596         } else {
597                 v.eh.Insert(v.Cursor.Loc(), "\t")
598                 v.Cursor.Right()
599         }
600         return true
601 }
602
603 // Save the buffer to disk
604 func (v *View) Save() bool {
605         // If this is an empty buffer, ask for a filename
606         if v.Buf.Path == "" {
607                 filename, canceled := messenger.Prompt("Filename: ")
608                 if !canceled {
609                         v.Buf.Path = filename
610                         v.Buf.Name = filename
611                 } else {
612                         return true
613                 }
614         }
615         err := v.Buf.Save()
616         if err != nil {
617                 messenger.Error(err.Error())
618         } else {
619                 messenger.Message("Saved " + v.Buf.Path)
620         }
621         return true
622 }
623
624 // Find opens a prompt and searches forward for the input
625 func (v *View) Find() bool {
626         if v.Cursor.HasSelection() {
627                 searchStart = v.Cursor.curSelection[1]
628         } else {
629                 searchStart = ToCharPos(v.Cursor.x, v.Cursor.y, v.Buf)
630         }
631         BeginSearch()
632         return true
633 }
634
635 // FindNext searches forwards for the last used search term
636 func (v *View) FindNext() bool {
637         if v.Cursor.HasSelection() {
638                 searchStart = v.Cursor.curSelection[1]
639         } else {
640                 searchStart = ToCharPos(v.Cursor.x, v.Cursor.y, v.Buf)
641         }
642         messenger.Message("Finding: " + lastSearch)
643         Search(lastSearch, v, true)
644         return true
645 }
646
647 // FindPrevious searches backwards for the last used search term
648 func (v *View) FindPrevious() bool {
649         if v.Cursor.HasSelection() {
650                 searchStart = v.Cursor.curSelection[0]
651         } else {
652                 searchStart = ToCharPos(v.Cursor.x, v.Cursor.y, v.Buf)
653         }
654         messenger.Message("Finding: " + lastSearch)
655         Search(lastSearch, v, false)
656         return true
657 }
658
659 // Undo undoes the last action
660 func (v *View) Undo() bool {
661         v.eh.Undo()
662         return true
663 }
664
665 // Redo redoes the last action
666 func (v *View) Redo() bool {
667         v.eh.Redo()
668         return true
669 }
670
671 // Copy the selection to the system clipboard
672 func (v *View) Copy() bool {
673         if v.Cursor.HasSelection() {
674                 clipboard.WriteAll(v.Cursor.GetSelection())
675                 v.freshClip = true
676                 messenger.Message("Copied selection to clipboard")
677         }
678         return true
679 }
680
681 // CutLine cuts the current line to the clipboard
682 func (v *View) CutLine() bool {
683         v.Cursor.SelectLine()
684         if v.freshClip == true {
685
686                 if v.Cursor.HasSelection() {
687                         if clip, err := clipboard.ReadAll(); err != nil {
688                                 messenger.Error(err)
689                         } else {
690                                 clipboard.WriteAll(clip + v.Cursor.GetSelection())
691                         }
692                 }
693         } else if time.Since(v.lastCutTime)/time.Second > 10*time.Second || v.freshClip == false {
694                 v.Copy()
695         }
696         v.freshClip = true
697         v.lastCutTime = time.Now()
698         v.Cursor.DeleteSelection()
699         v.Cursor.ResetSelection()
700         return true
701 }
702
703 // Cut the selection to the system clipboard
704 func (v *View) Cut() bool {
705         if v.Cursor.HasSelection() {
706                 clipboard.WriteAll(v.Cursor.GetSelection())
707                 v.Cursor.DeleteSelection()
708                 v.Cursor.ResetSelection()
709                 v.freshClip = true
710         }
711         return true
712 }
713
714 // Paste whatever is in the system clipboard into the buffer
715 // Delete and paste if the user has a selection
716 func (v *View) Paste() bool {
717         if v.Cursor.HasSelection() {
718                 v.Cursor.DeleteSelection()
719                 v.Cursor.ResetSelection()
720         }
721         clip, _ := clipboard.ReadAll()
722         v.eh.Insert(v.Cursor.Loc(), clip)
723         v.Cursor.SetLoc(v.Cursor.Loc() + Count(clip))
724         v.freshClip = false
725         return true
726 }
727
728 // SelectAll selects the entire buffer
729 func (v *View) SelectAll() bool {
730         v.Cursor.curSelection[0] = 0
731         v.Cursor.curSelection[1] = v.Buf.Len()
732         // Put the cursor at the beginning
733         v.Cursor.x = 0
734         v.Cursor.y = 0
735         return true
736 }
737
738 // OpenFile opens a new file in the buffer
739 func (v *View) OpenFile() bool {
740         if v.CanClose("Continue? (yes, no, save) ") {
741                 filename, canceled := messenger.Prompt("File to open: ")
742                 if canceled {
743                         return true
744                 }
745                 home, _ := homedir.Dir()
746                 filename = strings.Replace(filename, "~", home, 1)
747                 file, err := ioutil.ReadFile(filename)
748
749                 if err != nil {
750                         messenger.Error(err.Error())
751                         return true
752                 }
753                 buf := NewBuffer(string(file), filename)
754                 v.OpenBuffer(buf)
755         }
756         return true
757 }
758
759 // Start moves the viewport to the start of the buffer
760 func (v *View) Start() bool {
761         v.Topline = 0
762         return false
763 }
764
765 // End moves the viewport to the end of the buffer
766 func (v *View) End() bool {
767         if v.height > v.Buf.NumLines {
768                 v.Topline = 0
769         } else {
770                 v.Topline = v.Buf.NumLines - v.height
771         }
772         return false
773 }
774
775 // PageUp scrolls the view up a page
776 func (v *View) PageUp() bool {
777         if v.Topline > v.height {
778                 v.ScrollUp(v.height)
779         } else {
780                 v.Topline = 0
781         }
782         return false
783 }
784
785 // PageDown scrolls the view down a page
786 func (v *View) PageDown() bool {
787         if v.Buf.NumLines-(v.Topline+v.height) > v.height {
788                 v.ScrollDown(v.height)
789         } else if v.Buf.NumLines >= v.height {
790                 v.Topline = v.Buf.NumLines - v.height
791         }
792         return false
793 }
794
795 // HalfPageUp scrolls the view up half a page
796 func (v *View) HalfPageUp() bool {
797         if v.Topline > v.height/2 {
798                 v.ScrollUp(v.height / 2)
799         } else {
800                 v.Topline = 0
801         }
802         return false
803 }
804
805 // HalfPageDown scrolls the view down half a page
806 func (v *View) HalfPageDown() bool {
807         if v.Buf.NumLines-(v.Topline+v.height) > v.height/2 {
808                 v.ScrollDown(v.height / 2)
809         } else {
810                 if v.Buf.NumLines >= v.height {
811                         v.Topline = v.Buf.NumLines - v.height
812                 }
813         }
814         return false
815 }
816
817 // ToggleRuler turns line numbers off and on
818 func (v *View) ToggleRuler() bool {
819         if settings["ruler"] == false {
820                 settings["ruler"] = true
821         } else {
822                 settings["ruler"] = false
823         }
824         return false
825 }
826
827 // JumpLine jumps to a line and moves the view accordingly.
828 func (v *View) JumpLine() bool {
829         // Prompt for line number
830         linestring, canceled := messenger.Prompt("Jump to line # ")
831         if canceled {
832                 return false
833         }
834         lineint, err := strconv.Atoi(linestring)
835         lineint = lineint - 1 // fix offset
836         if err != nil {
837                 messenger.Error(err) // return errors
838                 return false
839         }
840         // Move cursor and view if possible.
841         if lineint < v.Buf.NumLines {
842                 v.Cursor.x = 0
843                 v.Cursor.y = lineint
844                 return true
845         }
846         messenger.Error("Only ", v.Buf.NumLines, " lines to jump")
847         return false
848 }
849
850 // None is no action
851 func None() bool {
852         return false
853 }