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