From 2adaf68bd438ffe648bf17bb5d912d2e628250a3 Mon Sep 17 00:00:00 2001 From: Zachary Yedidia Date: Sat, 23 Apr 2016 18:25:08 -0400 Subject: [PATCH] Rework the bindings as keys bound to actions --- cmd/micro/bindings.go | 532 ++++++++++++++++++++++++++++++++++++++++++ cmd/micro/micro.go | 1 + cmd/micro/view.go | 296 +---------------------- 3 files changed, 541 insertions(+), 288 deletions(-) create mode 100644 cmd/micro/bindings.go diff --git a/cmd/micro/bindings.go b/cmd/micro/bindings.go new file mode 100644 index 00000000..3f7374b8 --- /dev/null +++ b/cmd/micro/bindings.go @@ -0,0 +1,532 @@ +package main + +import ( + "io/ioutil" + "strings" + + "github.com/atotto/clipboard" + "github.com/gdamore/tcell" + "github.com/mitchellh/go-homedir" +) + +var bindings map[tcell.Key]func(*View) bool + +// InitBindings initializes the keybindings for micro +func InitBindings() { + bindings = make(map[tcell.Key]func(*View) bool) + + actions := map[string]func(*View) bool{ + "CursorUp": CursorUp, + "CursorDown": CursorDown, + "CursorLeft": CursorLeft, + "CursorRight": CursorRight, + "InsertEnter": InsertEnter, + "InsertSpace": InsertSpace, + "Backspace": Backspace, + "InsertTab": InsertTab, + "Save": Save, + "Find": Find, + "FindNext": FindNext, + "FindPrevious": FindPrevious, + "Undo": Undo, + "Redo": Redo, + "Copy": Copy, + "Cut": Cut, + "Paste": Paste, + "SelectAll": SelectAll, + "OpenFile": OpenFile, + "Beginning": Beginning, + "End": End, + "PageUp": PageUp, + "PageDown": PageDown, + "HalfPageUp": HalfPageUp, + "HalfPageDown": HalfPageDown, + "ToggleRuler": ToggleRuler, + } + + keys := map[string]tcell.Key{ + "Up": tcell.KeyUp, + "Down": tcell.KeyDown, + "Right": tcell.KeyRight, + "Left": tcell.KeyLeft, + "UpLeft": tcell.KeyUpLeft, + "UpRight": tcell.KeyUpRight, + "DownLeft": tcell.KeyDownLeft, + "DownRight": tcell.KeyDownRight, + "Center": tcell.KeyCenter, + "PgUp": tcell.KeyPgUp, + "PgDn": tcell.KeyPgDn, + "Home": tcell.KeyHome, + "End": tcell.KeyEnd, + "Insert": tcell.KeyInsert, + "Delete": tcell.KeyDelete, + "Help": tcell.KeyHelp, + "Exit": tcell.KeyExit, + "Clear": tcell.KeyClear, + "Cancel": tcell.KeyCancel, + "Print": tcell.KeyPrint, + "Pause": tcell.KeyPause, + "Backtab": tcell.KeyBacktab, + "F1": tcell.KeyF1, + "F2": tcell.KeyF2, + "F3": tcell.KeyF3, + "F4": tcell.KeyF4, + "F5": tcell.KeyF5, + "F6": tcell.KeyF6, + "F7": tcell.KeyF7, + "F8": tcell.KeyF8, + "F9": tcell.KeyF9, + "F10": tcell.KeyF10, + "F11": tcell.KeyF11, + "F12": tcell.KeyF12, + "F13": tcell.KeyF13, + "F14": tcell.KeyF14, + "F15": tcell.KeyF15, + "F16": tcell.KeyF16, + "F17": tcell.KeyF17, + "F18": tcell.KeyF18, + "F19": tcell.KeyF19, + "F20": tcell.KeyF20, + "F21": tcell.KeyF21, + "F22": tcell.KeyF22, + "F23": tcell.KeyF23, + "F24": tcell.KeyF24, + "F25": tcell.KeyF25, + "F26": tcell.KeyF26, + "F27": tcell.KeyF27, + "F28": tcell.KeyF28, + "F29": tcell.KeyF29, + "F30": tcell.KeyF30, + "F31": tcell.KeyF31, + "F32": tcell.KeyF32, + "F33": tcell.KeyF33, + "F34": tcell.KeyF34, + "F35": tcell.KeyF35, + "F36": tcell.KeyF36, + "F37": tcell.KeyF37, + "F38": tcell.KeyF38, + "F39": tcell.KeyF39, + "F40": tcell.KeyF40, + "F41": tcell.KeyF41, + "F42": tcell.KeyF42, + "F43": tcell.KeyF43, + "F44": tcell.KeyF44, + "F45": tcell.KeyF45, + "F46": tcell.KeyF46, + "F47": tcell.KeyF47, + "F48": tcell.KeyF48, + "F49": tcell.KeyF49, + "F50": tcell.KeyF50, + "F51": tcell.KeyF51, + "F52": tcell.KeyF52, + "F53": tcell.KeyF53, + "F54": tcell.KeyF54, + "F55": tcell.KeyF55, + "F56": tcell.KeyF56, + "F57": tcell.KeyF57, + "F58": tcell.KeyF58, + "F59": tcell.KeyF59, + "F60": tcell.KeyF60, + "F61": tcell.KeyF61, + "F62": tcell.KeyF62, + "F63": tcell.KeyF63, + "F64": tcell.KeyF64, + "CtrlSpace": tcell.KeyCtrlSpace, + "CtrlA": tcell.KeyCtrlA, + "CtrlB": tcell.KeyCtrlB, + "CtrlC": tcell.KeyCtrlC, + "CtrlD": tcell.KeyCtrlD, + "CtrlE": tcell.KeyCtrlE, + "CtrlF": tcell.KeyCtrlF, + "CtrlG": tcell.KeyCtrlG, + "CtrlH": tcell.KeyCtrlH, + "CtrlI": tcell.KeyCtrlI, + "CtrlJ": tcell.KeyCtrlJ, + "CtrlK": tcell.KeyCtrlK, + "CtrlL": tcell.KeyCtrlL, + "CtrlM": tcell.KeyCtrlM, + "CtrlN": tcell.KeyCtrlN, + "CtrlO": tcell.KeyCtrlO, + "CtrlP": tcell.KeyCtrlP, + "CtrlQ": tcell.KeyCtrlQ, + "CtrlR": tcell.KeyCtrlR, + "CtrlS": tcell.KeyCtrlS, + "CtrlT": tcell.KeyCtrlT, + "CtrlU": tcell.KeyCtrlU, + "CtrlV": tcell.KeyCtrlV, + "CtrlW": tcell.KeyCtrlW, + "CtrlX": tcell.KeyCtrlX, + "CtrlY": tcell.KeyCtrlY, + "CtrlZ": tcell.KeyCtrlZ, + "CtrlLeftSq": tcell.KeyCtrlLeftSq, + "CtrlBackslash": tcell.KeyCtrlBackslash, + "CtrlRightSq": tcell.KeyCtrlRightSq, + "CtrlCarat": tcell.KeyCtrlCarat, + "CtrlUnderscore": tcell.KeyCtrlUnderscore, + "Backspace": tcell.KeyBackspace, + "Tab": tcell.KeyTab, + "Esc": tcell.KeyEsc, + "Escape": tcell.KeyEscape, + "Enter": tcell.KeyEnter, + "Space": tcell.KeySpace, + "Backspace2": tcell.KeyBackspace2, + } + + bindings[keys["Up"]] = actions["CursorUp"] + bindings[keys["Down"]] = actions["CursorDown"] + bindings[keys["Right"]] = actions["CursorRight"] + bindings[keys["Left"]] = actions["CursorLeft"] + bindings[keys["Enter"]] = actions["InsertEnter"] + bindings[keys["Space"]] = actions["InsertSpace"] + bindings[keys["Backspace"]] = actions["Backspace"] + bindings[keys["Backspace2"]] = actions["Backspace"] + bindings[keys["Tab"]] = actions["InsertTab"] + bindings[keys["CtrlO"]] = actions["OpenFile"] + bindings[keys["CtrlS"]] = actions["Save"] + bindings[keys["CtrlF"]] = actions["Find"] + bindings[keys["CtrlN"]] = actions["FindNext"] + bindings[keys["CtrlP"]] = actions["FindPrevious"] + bindings[keys["CtrlZ"]] = actions["Undo"] + bindings[keys["CtrlY"]] = actions["Redo"] + bindings[keys["CtrlC"]] = actions["Copy"] + bindings[keys["CtrlX"]] = actions["Cut"] + bindings[keys["CtrlV"]] = actions["Paste"] + bindings[keys["CtrlA"]] = actions["SelectAll"] + bindings[keys["Home"]] = actions["Beginning"] + bindings[keys["End"]] = actions["End"] + bindings[keys["PageUp"]] = actions["PageUp"] + bindings[keys["PageDown"]] = actions["PageDown"] + bindings[keys["CtrlU"]] = actions["HalfPageUp"] + bindings[keys["CtrlD"]] = actions["HalfPageDown"] + bindings[keys["CtrlR"]] = actions["ToggleRuler"] +} + +// CursorUp moves the cursor up +func CursorUp(v *View) bool { + v.cursor.ResetSelection() + v.cursor.Up() + return true +} + +// CursorDown moves the cursor down +func CursorDown(v *View) bool { + v.cursor.ResetSelection() + v.cursor.Down() + return true +} + +// CursorLeft moves the cursor left +func CursorLeft(v *View) bool { + v.cursor.ResetSelection() + v.cursor.Left() + return true +} + +// CursorRight moves the cursor right +func CursorRight(v *View) bool { + v.cursor.ResetSelection() + v.cursor.Right() + return true +} + +// InsertSpace inserts a space +func InsertSpace(v *View) bool { + // Insert a space + if v.cursor.HasSelection() { + v.cursor.DeleteSelection() + v.cursor.ResetSelection() + } + v.eh.Insert(v.cursor.Loc(), " ") + v.cursor.Right() + return true +} + +// InsertEnter inserts a newline plus possible some whitespace if autoindent is on +func InsertEnter(v *View) bool { + // Insert a newline + if v.cursor.HasSelection() { + v.cursor.DeleteSelection() + v.cursor.ResetSelection() + } + + v.eh.Insert(v.cursor.Loc(), "\n") + ws := GetLeadingWhitespace(v.buf.lines[v.cursor.y]) + v.cursor.Right() + + if settings.AutoIndent { + v.eh.Insert(v.cursor.Loc(), ws) + for i := 0; i < len(ws); i++ { + v.cursor.Right() + } + } + v.cursor.lastVisualX = v.cursor.GetVisualX() + return true +} + +// Backspace deletes the previous character +func Backspace(v *View) bool { + // Delete a character + if v.cursor.HasSelection() { + v.cursor.DeleteSelection() + v.cursor.ResetSelection() + } else if v.cursor.Loc() > 0 { + // We have to do something a bit hacky here because we want to + // delete the line by first moving left and then deleting backwards + // but the undo redo would place the cursor in the wrong place + // So instead we move left, save the position, move back, delete + // and restore the position + + // If the user is using spaces instead of tabs and they are deleting + // whitespace at the start of the line, we should delete as if its a + // tab (tabSize number of spaces) + lineStart := v.buf.lines[v.cursor.y][:v.cursor.x] + if settings.TabsToSpaces && IsSpaces(lineStart) && len(lineStart) != 0 && len(lineStart)%settings.TabSize == 0 { + loc := v.cursor.Loc() + v.cursor.SetLoc(loc - settings.TabSize) + cx, cy := v.cursor.x, v.cursor.y + v.cursor.SetLoc(loc) + v.eh.Remove(loc-settings.TabSize, loc) + v.cursor.x, v.cursor.y = cx, cy + } else { + v.cursor.Left() + cx, cy := v.cursor.x, v.cursor.y + v.cursor.Right() + loc := v.cursor.Loc() + v.eh.Remove(loc-1, loc) + v.cursor.x, v.cursor.y = cx, cy + } + } + v.cursor.lastVisualX = v.cursor.GetVisualX() + return true +} + +// InsertTab inserts a tab or spaces +func InsertTab(v *View) bool { + // Insert a tab + if v.cursor.HasSelection() { + v.cursor.DeleteSelection() + v.cursor.ResetSelection() + } + if settings.TabsToSpaces { + v.eh.Insert(v.cursor.Loc(), Spaces(settings.TabSize)) + for i := 0; i < settings.TabSize; i++ { + v.cursor.Right() + } + } else { + v.eh.Insert(v.cursor.Loc(), "\t") + v.cursor.Right() + } + return true +} + +// Save the buffer to disk +func Save(v *View) bool { + // If this is an empty buffer, ask for a filename + if v.buf.path == "" { + filename, canceled := messenger.Prompt("Filename: ") + if !canceled { + v.buf.path = filename + v.buf.name = filename + } else { + return true + } + } + err := v.buf.Save() + if err != nil { + messenger.Error(err.Error()) + } else { + messenger.Message("Saved " + v.buf.path) + } + return true +} + +// Find opens a prompt and searches forward for the input +func Find(v *View) bool { + if v.cursor.HasSelection() { + searchStart = v.cursor.curSelection[1] + } else { + searchStart = ToCharPos(v.cursor.x, v.cursor.y, v.buf) + } + BeginSearch() + return true +} + +// FindNext searches forwards for the last used search term +func FindNext(v *View) bool { + if v.cursor.HasSelection() { + searchStart = v.cursor.curSelection[1] + } else { + searchStart = ToCharPos(v.cursor.x, v.cursor.y, v.buf) + } + messenger.Message("Find: " + lastSearch) + Search(lastSearch, v, true) + return true +} + +// FindPrevious searches backwards for the last used search term +func FindPrevious(v *View) bool { + if v.cursor.HasSelection() { + searchStart = v.cursor.curSelection[0] + } else { + searchStart = ToCharPos(v.cursor.x, v.cursor.y, v.buf) + } + messenger.Message("Find: " + lastSearch) + Search(lastSearch, v, false) + return true +} + +// Undo undoes the last action +func Undo(v *View) bool { + v.eh.Undo() + return true +} + +// Redo redoes the last action +func Redo(v *View) bool { + v.eh.Redo() + return true +} + +// Copy the selection to the system clipboard +func Copy(v *View) bool { + if v.cursor.HasSelection() { + if !clipboard.Unsupported { + clipboard.WriteAll(v.cursor.GetSelection()) + } else { + messenger.Error("Clipboard is not supported on your system") + } + } + return true +} + +// Cut the selection to the system clipboard +func Cut(v *View) bool { + if v.cursor.HasSelection() { + if !clipboard.Unsupported { + clipboard.WriteAll(v.cursor.GetSelection()) + v.cursor.DeleteSelection() + v.cursor.ResetSelection() + } else { + messenger.Error("Clipboard is not supported on your system") + } + } + return true +} + +// Paste whatever is in the system clipboard into the buffer +// Delete and paste if the user has a selection +func Paste(v *View) bool { + if !clipboard.Unsupported { + if v.cursor.HasSelection() { + v.cursor.DeleteSelection() + v.cursor.ResetSelection() + } + clip, _ := clipboard.ReadAll() + v.eh.Insert(v.cursor.Loc(), clip) + v.cursor.SetLoc(v.cursor.Loc() + Count(clip)) + } else { + messenger.Error("Clipboard is not supported on your system") + } + return true +} + +// SelectAll selects the entire buffer +func SelectAll(v *View) bool { + v.cursor.curSelection[1] = 0 + v.cursor.curSelection[0] = v.buf.Len() + // Put the cursor at the beginning + v.cursor.x = 0 + v.cursor.y = 0 + return true +} + +// OpenFile opens a new file in the buffer +func OpenFile(v *View) bool { + if v.CanClose("Continue? (yes, no, save) ") { + filename, canceled := messenger.Prompt("File to open: ") + if canceled { + return true + } + home, _ := homedir.Dir() + filename = strings.Replace(filename, "~", home, 1) + file, err := ioutil.ReadFile(filename) + + if err != nil { + messenger.Error(err.Error()) + return true + } + buf := NewBuffer(string(file), filename) + v.OpenBuffer(buf) + } + return true +} + +// Beginning moves the viewport to the start of the buffer +func Beginning(v *View) bool { + v.topline = 0 + return false +} + +// End moves the viewport to the end of the buffer +func End(v *View) bool { + if v.height > len(v.buf.lines) { + v.topline = 0 + } else { + v.topline = len(v.buf.lines) - v.height + } + return false +} + +// PageUp scrolls the view up a page +func PageUp(v *View) bool { + if v.topline > v.height { + v.ScrollUp(v.height) + } else { + v.topline = 0 + } + return false +} + +// PageDown scrolls the view down a page +func PageDown(v *View) bool { + if len(v.buf.lines)-(v.topline+v.height) > v.height { + v.ScrollDown(v.height) + } else { + if len(v.buf.lines) >= v.height { + v.topline = len(v.buf.lines) - v.height + } + } + return false +} + +// HalfPageUp scrolls the view up half a page +func HalfPageUp(v *View) bool { + if v.topline > v.height/2 { + v.ScrollUp(v.height / 2) + } else { + v.topline = 0 + } + return false +} + +// HalfPageDown scrolls the view down half a page +func HalfPageDown(v *View) bool { + if len(v.buf.lines)-(v.topline+v.height) > v.height/2 { + v.ScrollDown(v.height / 2) + } else { + if len(v.buf.lines) >= v.height { + v.topline = len(v.buf.lines) - v.height + } + } + return false +} + +// ToggleRuler turns line numbers off and on +func ToggleRuler(v *View) bool { + if settings.Ruler == false { + settings.Ruler = true + } else { + settings.Ruler = false + } + return false +} diff --git a/cmd/micro/micro.go b/cmd/micro/micro.go index 4665220b..cef6c976 100644 --- a/cmd/micro/micro.go +++ b/cmd/micro/micro.go @@ -182,6 +182,7 @@ func main() { InitConfigDir() // Load the user's settings InitSettings() + InitBindings() // Load the syntax files, including the colorscheme LoadSyntaxFiles() diff --git a/cmd/micro/view.go b/cmd/micro/view.go index 98b45b4c..55f7214a 100644 --- a/cmd/micro/view.go +++ b/cmd/micro/view.go @@ -1,14 +1,11 @@ package main import ( - "io/ioutil" "strconv" "strings" "time" - "github.com/atotto/clipboard" "github.com/gdamore/tcell" - "github.com/mitchellh/go-homedir" ) // The View struct stores information about a view into a buffer. @@ -121,46 +118,6 @@ func (v *View) ScrollDown(n int) { } } -// PageUp scrolls the view up a page -func (v *View) PageUp() { - if v.topline > v.height { - v.ScrollUp(v.height) - } else { - v.topline = 0 - } -} - -// PageDown scrolls the view down a page -func (v *View) PageDown() { - if len(v.buf.lines)-(v.topline+v.height) > v.height { - v.ScrollDown(v.height) - } else { - if len(v.buf.lines) >= v.height { - v.topline = len(v.buf.lines) - v.height - } - } -} - -// HalfPageUp scrolls the view up half a page -func (v *View) HalfPageUp() { - if v.topline > v.height/2 { - v.ScrollUp(v.height / 2) - } else { - v.topline = 0 - } -} - -// HalfPageDown scrolls the view down half a page -func (v *View) HalfPageDown() { - if len(v.buf.lines)-(v.topline+v.height) > v.height/2 { - v.ScrollDown(v.height / 2) - } else { - if len(v.buf.lines) >= v.height { - v.topline = len(v.buf.lines) - v.height - } - } -} - // CanClose returns whether or not the view can be closed // If there are unsaved changes, the user will be asked if the view can be closed // causing them to lose the unsaved changes @@ -172,7 +129,7 @@ func (v *View) CanClose(msg string) bool { if strings.ToLower(quit) == "yes" || strings.ToLower(quit) == "y" { return true } else if strings.ToLower(quit) == "save" || strings.ToLower(quit) == "s" { - v.Save() + Save(v) return true } } @@ -182,75 +139,6 @@ func (v *View) CanClose(msg string) bool { return false } -// Save the buffer to disk -func (v *View) Save() { - // If this is an empty buffer, ask for a filename - if v.buf.path == "" { - filename, canceled := messenger.Prompt("Filename: ") - if !canceled { - v.buf.path = filename - v.buf.name = filename - } else { - return - } - } - err := v.buf.Save() - if err != nil { - messenger.Error(err.Error()) - } else { - messenger.Message("Saved " + v.buf.path) - } -} - -// Copy the selection to the system clipboard -func (v *View) Copy() { - if v.cursor.HasSelection() { - if !clipboard.Unsupported { - clipboard.WriteAll(v.cursor.GetSelection()) - } else { - messenger.Error("Clipboard is not supported on your system") - } - } -} - -// Cut the selection to the system clipboard -func (v *View) Cut() { - if v.cursor.HasSelection() { - if !clipboard.Unsupported { - clipboard.WriteAll(v.cursor.GetSelection()) - v.cursor.DeleteSelection() - v.cursor.ResetSelection() - } else { - messenger.Error("Clipboard is not supported on your system") - } - } -} - -// Paste whatever is in the system clipboard into the buffer -// Delete and paste if the user has a selection -func (v *View) Paste() { - if !clipboard.Unsupported { - if v.cursor.HasSelection() { - v.cursor.DeleteSelection() - v.cursor.ResetSelection() - } - clip, _ := clipboard.ReadAll() - v.eh.Insert(v.cursor.Loc(), clip) - v.cursor.SetLoc(v.cursor.Loc() + Count(clip)) - } else { - messenger.Error("Clipboard is not supported on your system") - } -} - -// SelectAll selects the entire buffer -func (v *View) SelectAll() { - v.cursor.curSelection[1] = 0 - v.cursor.curSelection[0] = v.buf.Len() - // Put the cursor at the beginning - v.cursor.x = 0 - v.cursor.y = 0 -} - // OpenBuffer opens a new buffer in this view. // This resets the topline, event handler and cursor. func (v *View) OpenBuffer(buf *Buffer) { @@ -277,22 +165,6 @@ func (v *View) OpenBuffer(buf *Buffer) { // OpenFile opens a new file in the current view // It makes sure that the current buffer can be closed first (unsaved changes) func (v *View) OpenFile() { - if v.CanClose("Continue? (yes, no, save) ") { - filename, canceled := messenger.Prompt("File to open: ") - if canceled { - return - } - home, _ := homedir.Dir() - filename = strings.Replace(filename, "~", home, 1) - file, err := ioutil.ReadFile(filename) - - if err != nil { - messenger.Error(err.Error()) - return - } - buf := NewBuffer(string(file), filename) - v.OpenBuffer(buf) - } } // Relocate moves the view window so that the cursor is in view @@ -358,165 +230,7 @@ func (v *View) HandleEvent(event tcell.Event) { // Window resized v.Resize(e.Size()) case *tcell.EventKey: - switch e.Key() { - case tcell.KeyUp: - // Cursor up - v.cursor.ResetSelection() - v.cursor.Up() - case tcell.KeyDown: - // Cursor down - v.cursor.ResetSelection() - v.cursor.Down() - case tcell.KeyLeft: - // Cursor left - v.cursor.ResetSelection() - v.cursor.Left() - case tcell.KeyRight: - // Cursor right - v.cursor.ResetSelection() - v.cursor.Right() - case tcell.KeyEnter: - // Insert a newline - if v.cursor.HasSelection() { - v.cursor.DeleteSelection() - v.cursor.ResetSelection() - } - - v.eh.Insert(v.cursor.Loc(), "\n") - ws := GetLeadingWhitespace(v.buf.lines[v.cursor.y]) - v.cursor.Right() - - if settings.AutoIndent { - v.eh.Insert(v.cursor.Loc(), ws) - for i := 0; i < len(ws); i++ { - v.cursor.Right() - } - } - v.cursor.lastVisualX = v.cursor.GetVisualX() - case tcell.KeySpace: - // Insert a space - if v.cursor.HasSelection() { - v.cursor.DeleteSelection() - v.cursor.ResetSelection() - } - v.eh.Insert(v.cursor.Loc(), " ") - v.cursor.Right() - case tcell.KeyBackspace2, tcell.KeyBackspace: - // Delete a character - if v.cursor.HasSelection() { - v.cursor.DeleteSelection() - v.cursor.ResetSelection() - } else if v.cursor.Loc() > 0 { - // We have to do something a bit hacky here because we want to - // delete the line by first moving left and then deleting backwards - // but the undo redo would place the cursor in the wrong place - // So instead we move left, save the position, move back, delete - // and restore the position - - // If the user is using spaces instead of tabs and they are deleting - // whitespace at the start of the line, we should delete as if its a - // tab (tabSize number of spaces) - lineStart := v.buf.lines[v.cursor.y][:v.cursor.x] - if settings.TabsToSpaces && IsSpaces(lineStart) && len(lineStart) != 0 && len(lineStart)%settings.TabSize == 0 { - loc := v.cursor.Loc() - v.cursor.SetLoc(loc - settings.TabSize) - cx, cy := v.cursor.x, v.cursor.y - v.cursor.SetLoc(loc) - v.eh.Remove(loc-settings.TabSize, loc) - v.cursor.x, v.cursor.y = cx, cy - } else { - v.cursor.Left() - cx, cy := v.cursor.x, v.cursor.y - v.cursor.Right() - loc := v.cursor.Loc() - v.eh.Remove(loc-1, loc) - v.cursor.x, v.cursor.y = cx, cy - } - } - v.cursor.lastVisualX = v.cursor.GetVisualX() - case tcell.KeyTab: - // Insert a tab - if v.cursor.HasSelection() { - v.cursor.DeleteSelection() - v.cursor.ResetSelection() - } - if settings.TabsToSpaces { - v.eh.Insert(v.cursor.Loc(), Spaces(settings.TabSize)) - for i := 0; i < settings.TabSize; i++ { - v.cursor.Right() - } - } else { - v.eh.Insert(v.cursor.Loc(), "\t") - v.cursor.Right() - } - case tcell.KeyCtrlS: - v.Save() - case tcell.KeyCtrlF: - if v.cursor.HasSelection() { - searchStart = v.cursor.curSelection[1] - } else { - searchStart = ToCharPos(v.cursor.x, v.cursor.y, v.buf) - } - BeginSearch() - case tcell.KeyCtrlN: - if v.cursor.HasSelection() { - searchStart = v.cursor.curSelection[1] - } else { - searchStart = ToCharPos(v.cursor.x, v.cursor.y, v.buf) - } - messenger.Message("Find: " + lastSearch) - Search(lastSearch, v, true) - case tcell.KeyCtrlP: - if v.cursor.HasSelection() { - searchStart = v.cursor.curSelection[0] - } else { - searchStart = ToCharPos(v.cursor.x, v.cursor.y, v.buf) - } - messenger.Message("Find: " + lastSearch) - Search(lastSearch, v, false) - case tcell.KeyCtrlZ: - v.eh.Undo() - case tcell.KeyCtrlY: - v.eh.Redo() - case tcell.KeyCtrlC: - v.Copy() - case tcell.KeyCtrlX: - v.Cut() - case tcell.KeyCtrlV: - v.Paste() - case tcell.KeyCtrlA: - v.SelectAll() - case tcell.KeyCtrlO: - v.OpenFile() - case tcell.KeyHome: - v.topline = 0 - relocate = false - case tcell.KeyEnd: - if v.height > len(v.buf.lines) { - v.topline = 0 - } else { - v.topline = len(v.buf.lines) - v.height - } - relocate = false - case tcell.KeyPgUp: - v.PageUp() - relocate = false - case tcell.KeyPgDn: - v.PageDown() - relocate = false - case tcell.KeyCtrlU: - v.HalfPageUp() - relocate = false - case tcell.KeyCtrlD: - v.HalfPageDown() - relocate = false - case tcell.KeyCtrlR: - if settings.Ruler == false { - settings.Ruler = true - } else { - settings.Ruler = false - } - case tcell.KeyRune: + if e.Key() == tcell.KeyRune { // Insert a character if v.cursor.HasSelection() { v.cursor.DeleteSelection() @@ -524,6 +238,12 @@ func (v *View) HandleEvent(event tcell.Event) { } v.eh.Insert(v.cursor.Loc(), string(e.Rune())) v.cursor.Right() + } else { + for key, action := range bindings { + if e.Key() == key { + relocate = action(v) + } + } } case *tcell.EventMouse: x, y := e.Position()