// 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 {
+ if c == nil {
+ continue
+ }
+ 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
}
}
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
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
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
}
// HandleEvent executes the tcell event properly
-// TODO: multiple actions bound to one key
func (h *BufPane) HandleEvent(event tcell.Event) {
+ if h.Buf.ExternallyModified() && !h.Buf.ReloadDisabled {
+ InfoBar.YNPrompt("The file on disk has changed. Reload file? (y,n,esc)", func(yes, canceled bool) {
+ if canceled {
+ h.Buf.DisableReload()
+ }
+ 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(),
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
// 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()
// 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
if recording_macro {
curmacro = append(curmacro, r)
}
+ h.Relocate()
h.PluginCBRune("onRune", r)
}
}
-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()
}
"DeleteWordLeft": (*BufPane).DeleteWordLeft,
"SelectLine": (*BufPane).SelectLine,
"SelectToStartOfLine": (*BufPane).SelectToStartOfLine,
+ "SelectToStartOfText": (*BufPane).SelectToStartOfText,
"SelectToEndOfLine": (*BufPane).SelectToEndOfLine,
"ParagraphPrevious": (*BufPane).ParagraphPrevious,
"ParagraphNext": (*BufPane).ParagraphNext,
"MoveLinesDown": (*BufPane).MoveLinesDown,
"IndentSelection": (*BufPane).IndentSelection,
"OutdentSelection": (*BufPane).OutdentSelection,
+ "Autocomplete": (*BufPane).Autocomplete,
+ "CycleAutocompleteBack": (*BufPane).CycleAutocompleteBack,
"OutdentLine": (*BufPane).OutdentLine,
+ "IndentLine": (*BufPane).IndentLine,
"Paste": (*BufPane).Paste,
"PastePrimary": (*BufPane).PastePrimary,
"SelectAll": (*BufPane).SelectAll,
"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,
"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,
// 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,
+ "IndentLine": true,
+ "Paste": true,
+ "PastePrimary": true,
+ "SelectPageUp": true,
+ "SelectPageDown": true,
+ "StartOfLine": true,
+ "StartOfText": true,
+ "EndOfLine": true,
+ "JumpToMatchingBrace": true,
}