X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=internal%2Faction%2Fbufpane.go;h=2cbbbab5c07b3a82c292a03f2df23f3511697f3f;hb=90304fb472fab5a9809feb0d9b23b0930619cf0d;hp=4f4876047366ab7029c93dc6a0138d38dbd54851;hpb=d2f8adb8ffd8239771e742d9eff3cdde8d46969b;p=micro.git diff --git a/internal/action/bufpane.go b/internal/action/bufpane.go index 4f487604..2cbbbab5 100644 --- a/internal/action/bufpane.go +++ b/internal/action/bufpane.go @@ -30,8 +30,14 @@ func init() { func LuaAction(fn string) func(*BufPane) bool { luaFn := strings.Split(fn, ".") + if len(luaFn) <= 1 { + return nil + } plName, plFn := luaFn[0], luaFn[1] pl := config.FindPlugin(plName) + if pl == nil { + return nil + } return func(h *BufPane) bool { val, err := pl.Call(plFn, luar.New(ulua.L, h)) if err != nil { @@ -47,34 +53,76 @@ func LuaAction(fn string) func(*BufPane) bool { // BufMapKey maps a key event to an action func BufMapKey(k Event, action string) { - actions := strings.SplitN(action, ",", -1) BufKeyStrings[k] = action - actionfns := make([]func(*BufPane) bool, len(actions)) - for i, a := range actions { - a = strings.TrimSpace(a) + var actionfns []func(*BufPane) bool + var names []string + var types []byte + for i := 0; ; i++ { + if action == "" { + break + } + + // TODO: fix problem when complex bindings have these + // characters (escape them?) + idx := strings.IndexAny(action, "&|,") + a := action + if idx >= 0 { + a = action[:idx] + types = append(types, action[idx]) + action = action[idx+1:] + } else { + types = append(types, ' ') + action = "" + } + var afn func(*BufPane) bool - if strings.HasPrefix(action, "command:") { + if strings.HasPrefix(a, "command:") { a = strings.SplitN(a, ":", 2)[1] afn = CommandAction(a) + names = append(names, "") } else if strings.HasPrefix(a, "command-edit:") { a = strings.SplitN(a, ":", 2)[1] afn = CommandEditAction(a) + names = append(names, "") } else if strings.HasPrefix(a, "lua:") { a = strings.SplitN(a, ":", 2)[1] afn = LuaAction(a) + if afn == nil { + screen.TermMessage("Lua Error:", a, "does not exist") + continue + } + split := strings.SplitN(a, ".", 2) + if len(split) > 1 { + a = strings.Title(split[0]) + strings.Title(split[1]) + } else { + a = strings.Title(a) + } + + names = append(names, a) } else if f, ok := BufKeyActions[a]; ok { afn = f + names = append(names, a) } else { - screen.TermMessage("Error:", action, "does not exist") + screen.TermMessage("Error:", a, "does not exist") + continue } - actionfns[i] = afn + actionfns = append(actionfns, afn) } BufKeyBindings[k] = func(h *BufPane) bool { - b := false - for _, a := range actionfns { - b = a(h) || b + cursors := h.Buf.GetCursors() + success := true + for i, a := range actionfns { + for j, c := range cursors { + h.Buf.SetCurCursor(c.Num) + h.Cursor = c + if i == 0 || (success && types[i-1] == '&') || (!success && types[i-1] == '|') || (types[i-1] == ',') { + success = h.execAction(a, names[i], j) + } else { + break + } + } } - return b + return true } } @@ -134,15 +182,17 @@ type BufPane struct { multiWord bool splitID uint64 + tab *Tab // remember original location of a search in case the search is canceled searchOrig buffer.Loc } -func NewBufPane(buf *buffer.Buffer, win display.BWindow) *BufPane { +func NewBufPane(buf *buffer.Buffer, win display.BWindow, tab *Tab) *BufPane { h := new(BufPane) h.Buf = buf h.BWindow = win + h.tab = tab h.Cursor = h.Buf.GetActiveCursor() h.mouseReleased = true @@ -152,9 +202,23 @@ func NewBufPane(buf *buffer.Buffer, win display.BWindow) *BufPane { return h } -func NewBufPaneFromBuf(buf *buffer.Buffer) *BufPane { +func NewBufPaneFromBuf(buf *buffer.Buffer, tab *Tab) *BufPane { w := display.NewBufWindow(0, 0, 0, 0, buf) - return NewBufPane(buf, w) + return NewBufPane(buf, w, tab) +} + +func (h *BufPane) SetTab(t *Tab) { + h.tab = t +} + +func (h *BufPane) Tab() *Tab { + return h.tab +} + +func (h *BufPane) ResizePane(size int) { + n := h.tab.GetNode(h.splitID) + n.ResizeSplit(size) + h.tab.Resize() } // PluginCB calls all plugin callbacks with a certain name and @@ -184,8 +248,6 @@ func (h *BufPane) OpenBuffer(b *buffer.Buffer) { h.BWindow.SetBuffer(b) h.Cursor = b.GetActiveCursor() h.Resize(h.GetView().Width, h.GetView().Height) - v := new(display.View) - h.SetView(v) h.Relocate() // Set mouseReleased to true because we assume the mouse is not being pressed when // the editor is opened @@ -209,14 +271,27 @@ func (h *BufPane) Name() string { } // HandleEvent executes the tcell event properly -// TODO: multiple actions bound to one key func (h *BufPane) HandleEvent(event tcell.Event) { + if h.Buf.ExternallyModified() { + InfoBar.YNPrompt("The file on disk has changed. Reload file? (y,n)", func(yes, canceled bool) { + if !yes || canceled { + h.Buf.UpdateModTime() + } else { + h.Buf.ReOpen() + } + }) + + } + switch e := event.(type) { case *tcell.EventRaw: re := RawEvent{ esc: e.EscSeq(), } h.DoKeyEvent(re) + case *tcell.EventPaste: + h.paste(e.Text()) + h.Relocate() case *tcell.EventKey: ke := KeyEvent{ code: e.Key(), @@ -229,14 +304,27 @@ func (h *BufPane) HandleEvent(event tcell.Event) { h.DoRuneInsert(e.Rune()) } case *tcell.EventMouse: + cancel := false switch e.Buttons() { + case tcell.Button1: + _, my := e.Position() + if h.Buf.Settings["statusline"].(bool) && my >= h.GetView().Y+h.GetView().Height-1 { + cancel = true + } case tcell.ButtonNone: // Mouse event with no click if !h.mouseReleased { // Mouse was just released - mx, my := e.Position() - mouseLoc := h.GetMouseLoc(buffer.Loc{X: mx, Y: my}) + // mx, my := e.Position() + // mouseLoc := h.LocFromVisual(buffer.Loc{X: mx, Y: my}) + + // we could finish the selection based on the release location as described + // below but when the mouse click is within the scroll margin this will + // cause a scroll and selection even for a simple mouse click which is + // not good + // for terminals that don't support mouse motion events, selection via + // the mouse won't work but this is ok // Relocating here isn't really necessary because the cursor will // be in the right place from the last mouse event @@ -244,20 +332,22 @@ func (h *BufPane) HandleEvent(event tcell.Event) { // events, this still allows the user to make selections, except only after they // release the mouse - if !h.doubleClick && !h.tripleClick { - h.Cursor.Loc = mouseLoc - h.Cursor.SetSelectionEnd(h.Cursor.Loc) - h.Cursor.CopySelection("primary") - } + // if !h.doubleClick && !h.tripleClick { + // h.Cursor.Loc = mouseLoc + // h.Cursor.SetSelectionEnd(h.Cursor.Loc) + // h.Cursor.CopySelection("primary") + // } h.mouseReleased = true } } - me := MouseEvent{ - btn: e.Buttons(), - mod: e.Modifiers(), + if !cancel { + me := MouseEvent{ + btn: e.Buttons(), + mod: e.Modifiers(), + } + h.DoMouseEvent(me, e) } - h.DoMouseEvent(me, e) } h.Buf.MergeCursors() @@ -282,46 +372,41 @@ func (h *BufPane) HandleEvent(event tcell.Event) { // to and executing it (possibly multiple times for multiple cursors) func (h *BufPane) DoKeyEvent(e Event) bool { if action, ok := BufKeyBindings[e]; ok { - estr := BufKeyStrings[e] - if estr != "InsertTab" { - h.Buf.HasSuggestions = false - } - for _, s := range MultiActions { - if s == estr { - cursors := h.Buf.GetCursors() - for _, c := range cursors { - h.Buf.SetCurCursor(c.Num) - h.Cursor = c - if !h.PluginCB("pre" + estr) { - // canceled by plugin - continue - } - rel := action(h) - if h.PluginCB("on"+estr) && rel { - h.Relocate() - } + return action(h) + } + return false +} - if recording_macro { - if estr != "ToggleMacro" && estr != "PlayMacro" { - curmacro = append(curmacro, e) - } +func (h *BufPane) execAction(action func(*BufPane) bool, name string, cursor int) bool { + if name != "Autocomplete" && name != "CycleAutocompleteBack" { + h.Buf.HasSuggestions = false + } + + _, isMulti := MultiActions[name] + if (!isMulti && cursor == 0) || isMulti { + if h.PluginCB("pre" + name) { + success := action(h) + success = success && h.PluginCB("on"+name) + + if isMulti { + if recording_macro { + if name != "ToggleMacro" && name != "PlayMacro" { + curmacro = append(curmacro, action) } } - return true } + + return success } - if !h.PluginCB("pre" + estr) { - return false - } - rel := action(h) - if h.PluginCB("on"+estr) && rel { - h.Relocate() - } - return true } + return false } +func (h *BufPane) completeAction(action string) { + h.PluginCB("on" + action) +} + func (h *BufPane) HasKeyEvent(e Event) bool { _, ok := BufKeyBindings[e] return ok @@ -371,22 +456,29 @@ func (h *BufPane) DoRuneInsert(r rune) { } } -func (h *BufPane) VSplitBuf(buf *buffer.Buffer) *BufPane { - e := NewBufPaneFromBuf(buf) - e.splitID = MainTab().GetNode(h.splitID).VSplit(h.Buf.Settings["splitright"].(bool)) +func (h *BufPane) VSplitIndex(buf *buffer.Buffer, right bool) *BufPane { + e := NewBufPaneFromBuf(buf, h.tab) + e.splitID = MainTab().GetNode(h.splitID).VSplit(right) MainTab().Panes = append(MainTab().Panes, e) MainTab().Resize() MainTab().SetActive(len(MainTab().Panes) - 1) return e } -func (h *BufPane) HSplitBuf(buf *buffer.Buffer) *BufPane { - e := NewBufPaneFromBuf(buf) - e.splitID = MainTab().GetNode(h.splitID).HSplit(h.Buf.Settings["splitbottom"].(bool)) +func (h *BufPane) HSplitIndex(buf *buffer.Buffer, bottom bool) *BufPane { + e := NewBufPaneFromBuf(buf, h.tab) + e.splitID = MainTab().GetNode(h.splitID).HSplit(bottom) MainTab().Panes = append(MainTab().Panes, e) MainTab().Resize() MainTab().SetActive(len(MainTab().Panes) - 1) return e } + +func (h *BufPane) VSplitBuf(buf *buffer.Buffer) *BufPane { + return h.VSplitIndex(buf, h.Buf.Settings["splitright"].(bool)) +} +func (h *BufPane) HSplitBuf(buf *buffer.Buffer) *BufPane { + return h.HSplitIndex(buf, h.Buf.Settings["splitbottom"].(bool)) +} func (h *BufPane) Close() { h.Buf.Close() } @@ -435,6 +527,7 @@ var BufKeyActions = map[string]BufKeyAction{ "DeleteWordLeft": (*BufPane).DeleteWordLeft, "SelectLine": (*BufPane).SelectLine, "SelectToStartOfLine": (*BufPane).SelectToStartOfLine, + "SelectToStartOfText": (*BufPane).SelectToStartOfText, "SelectToEndOfLine": (*BufPane).SelectToEndOfLine, "ParagraphPrevious": (*BufPane).ParagraphPrevious, "ParagraphNext": (*BufPane).ParagraphNext, @@ -460,6 +553,8 @@ var BufKeyActions = map[string]BufKeyAction{ "MoveLinesDown": (*BufPane).MoveLinesDown, "IndentSelection": (*BufPane).IndentSelection, "OutdentSelection": (*BufPane).OutdentSelection, + "Autocomplete": (*BufPane).Autocomplete, + "CycleAutocompleteBack": (*BufPane).CycleAutocompleteBack, "OutdentLine": (*BufPane).OutdentLine, "Paste": (*BufPane).Paste, "PastePrimary": (*BufPane).PastePrimary, @@ -473,10 +568,12 @@ var BufKeyActions = map[string]BufKeyAction{ "SelectPageDown": (*BufPane).SelectPageDown, "HalfPageUp": (*BufPane).HalfPageUp, "HalfPageDown": (*BufPane).HalfPageDown, + "StartOfText": (*BufPane).StartOfText, "StartOfLine": (*BufPane).StartOfLine, "EndOfLine": (*BufPane).EndOfLine, "ToggleHelp": (*BufPane).ToggleHelp, "ToggleKeyMenu": (*BufPane).ToggleKeyMenu, + "ToggleDiffGutter": (*BufPane).ToggleDiffGutter, "ToggleRuler": (*BufPane).ToggleRuler, "ClearStatus": (*BufPane).ClearStatus, "ShellMode": (*BufPane).ShellMode, @@ -499,11 +596,14 @@ var BufKeyActions = map[string]BufKeyAction{ "ScrollUp": (*BufPane).ScrollUpAction, "ScrollDown": (*BufPane).ScrollDownAction, "SpawnMultiCursor": (*BufPane).SpawnMultiCursor, + "SpawnMultiCursorUp": (*BufPane).SpawnMultiCursorUp, + "SpawnMultiCursorDown": (*BufPane).SpawnMultiCursorDown, "SpawnMultiCursorSelect": (*BufPane).SpawnMultiCursorSelect, "RemoveMultiCursor": (*BufPane).RemoveMultiCursor, "RemoveAllMultiCursors": (*BufPane).RemoveAllMultiCursors, "SkipMultiCursor": (*BufPane).SkipMultiCursor, "JumpToMatchingBrace": (*BufPane).JumpToMatchingBrace, + "None": (*BufPane).None, // This was changed to InsertNewline but I don't want to break backwards compatibility "InsertEnter": (*BufPane).InsertNewline, @@ -519,52 +619,54 @@ var BufMouseActions = map[string]BufMouseAction{ // times if there are multiple cursors (one per cursor) // Generally actions that modify global editor state like quitting or // saving should not be included in this list -var MultiActions = []string{ - "CursorUp", - "CursorDown", - "CursorPageUp", - "CursorPageDown", - "CursorLeft", - "CursorRight", - "CursorStart", - "CursorEnd", - "SelectToStart", - "SelectToEnd", - "SelectUp", - "SelectDown", - "SelectLeft", - "SelectRight", - "WordRight", - "WordLeft", - "SelectWordRight", - "SelectWordLeft", - "DeleteWordRight", - "DeleteWordLeft", - "SelectLine", - "SelectToStartOfLine", - "SelectToEndOfLine", - "ParagraphPrevious", - "ParagraphNext", - "InsertNewline", - "Backspace", - "Delete", - "InsertTab", - "FindNext", - "FindPrevious", - "Cut", - "CutLine", - "DuplicateLine", - "DeleteLine", - "MoveLinesUp", - "MoveLinesDown", - "IndentSelection", - "OutdentSelection", - "OutdentLine", - "Paste", - "PastePrimary", - "SelectPageUp", - "SelectPageDown", - "StartOfLine", - "EndOfLine", - "JumpToMatchingBrace", +var MultiActions = map[string]bool{ + "CursorUp": true, + "CursorDown": true, + "CursorPageUp": true, + "CursorPageDown": true, + "CursorLeft": true, + "CursorRight": true, + "CursorStart": true, + "CursorEnd": true, + "SelectToStart": true, + "SelectToEnd": true, + "SelectUp": true, + "SelectDown": true, + "SelectLeft": true, + "SelectRight": true, + "WordRight": true, + "WordLeft": true, + "SelectWordRight": true, + "SelectWordLeft": true, + "DeleteWordRight": true, + "DeleteWordLeft": true, + "SelectLine": true, + "SelectToStartOfLine": true, + "SelectToStartOfText": true, + "SelectToEndOfLine": true, + "ParagraphPrevious": true, + "ParagraphNext": true, + "InsertNewline": true, + "Backspace": true, + "Delete": true, + "InsertTab": true, + "FindNext": true, + "FindPrevious": true, + "Cut": true, + "CutLine": true, + "DuplicateLine": true, + "DeleteLine": true, + "MoveLinesUp": true, + "MoveLinesDown": true, + "IndentSelection": true, + "OutdentSelection": true, + "OutdentLine": true, + "Paste": true, + "PastePrimary": true, + "SelectPageUp": true, + "SelectPageDown": true, + "StartOfLine": true, + "StartOfText": true, + "EndOfLine": true, + "JumpToMatchingBrace": true, }