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