2018-08-28 02:53:08 +03:00
|
|
|
package action
|
|
|
|
|
|
|
|
import (
|
2019-01-20 01:14:51 +03:00
|
|
|
"strings"
|
2018-08-28 02:53:08 +03:00
|
|
|
"time"
|
|
|
|
|
2019-03-20 01:28:51 +03:00
|
|
|
luar "layeh.com/gopher-luar"
|
|
|
|
|
2019-08-04 01:49:05 +03:00
|
|
|
lua "github.com/yuin/gopher-lua"
|
2020-05-04 17:16:15 +03:00
|
|
|
"github.com/zyedidia/micro/v2/internal/buffer"
|
|
|
|
"github.com/zyedidia/micro/v2/internal/config"
|
|
|
|
"github.com/zyedidia/micro/v2/internal/display"
|
|
|
|
ulua "github.com/zyedidia/micro/v2/internal/lua"
|
|
|
|
"github.com/zyedidia/micro/v2/internal/screen"
|
Improve buffer view relocation after jumping to a far-away location (#2628)
* Improve buffer view relocation after jumping to a far-away location
When the cursor is moved to a location which is far away from the
current location (e.g. after a search or a goto line), the buffer view
is always relocated in such a way that the cursor is at the bottom or
at the top (minus scrollmargin), i.e. as if we just scrolled to this
location. It's not like in other editors, and IMHO it's annoying. When
we jump to a new location far away, we usually want to see more of its
context, so the cursor should be placed closer to the center of the
view, not near its edges.
This change implements the behavior similar to other editors:
- If the distance between the new and the old location is less than one
frame (i.e. the view either doesn't change or just slightly "shifts")
then the current behavior remains unchanged.
- Otherwise the current line is placed at 25% of the window height.
* Postpone calling onBufPaneOpen until the initial resize
It is currently not possible to find out the geometry of a newly created
bufpane in onBufPaneOpen lua callback: bp:GetView() returns {0,0,0,0}
instead of the actual window. The reason is that the bufpane view is not
properly initialized yet when the bufpane is created and the callback is
triggered. It is initialized a bit later, at the initial resize.
So postpone calling onBufPaneOpen until after the initial resize.
* Improve buffer view relocation when opening a file at a far-away location
When a file is opened with the initial cursor location at a given line
which is far away from the beginning of the file, the buffer view is
relocated so that the cursor is at the bottom (minus scrollmargin)
as if we just scrolled to this line, which is annoying since we'd rather
like to see more of the context of this initial location.
So implement the behavior similar to the earlier commit (which addresses
a similar issue about jumping far away after a search or goto):
- If the initial cursor location is less than one frame away from the
beginning of the buffer, keep the existing behavior i.e. just display
the beginning of the buffer.
- Otherwise place the cursor location at 25% of the window height.
2022-12-03 06:38:09 +03:00
|
|
|
"github.com/zyedidia/micro/v2/internal/util"
|
2020-09-05 21:52:35 +03:00
|
|
|
"github.com/zyedidia/tcell/v2"
|
2018-08-28 02:53:08 +03:00
|
|
|
)
|
|
|
|
|
2024-03-14 05:52:52 +03:00
|
|
|
type BufAction interface{}
|
|
|
|
|
2021-08-22 00:58:30 +03:00
|
|
|
// BufKeyAction represents an action bound to a key.
|
2019-01-19 23:37:59 +03:00
|
|
|
type BufKeyAction func(*BufPane) bool
|
2021-08-22 00:58:30 +03:00
|
|
|
|
|
|
|
// BufMouseAction is an action that must be bound to a mouse event.
|
2019-01-19 23:37:59 +03:00
|
|
|
type BufMouseAction func(*BufPane, *tcell.EventMouse) bool
|
2018-08-28 02:53:08 +03:00
|
|
|
|
2021-08-22 00:58:30 +03:00
|
|
|
// BufBindings stores the bindings for the buffer pane type.
|
2020-07-01 04:25:54 +03:00
|
|
|
var BufBindings *KeyTree
|
2018-08-28 02:53:08 +03:00
|
|
|
|
2021-08-22 00:58:30 +03:00
|
|
|
// BufKeyActionGeneral makes a general pane action from a BufKeyAction.
|
2020-07-01 04:25:54 +03:00
|
|
|
func BufKeyActionGeneral(a BufKeyAction) PaneKeyAction {
|
|
|
|
return func(p Pane) bool {
|
|
|
|
return a(p.(*BufPane))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-22 00:58:30 +03:00
|
|
|
// BufMouseActionGeneral makes a general pane mouse action from a BufKeyAction.
|
2020-07-01 04:25:54 +03:00
|
|
|
func BufMouseActionGeneral(a BufMouseAction) PaneMouseAction {
|
|
|
|
return func(p Pane, me *tcell.EventMouse) bool {
|
|
|
|
return a(p.(*BufPane), me)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-28 02:53:08 +03:00
|
|
|
func init() {
|
2020-07-01 04:25:54 +03:00
|
|
|
BufBindings = NewKeyTree()
|
2018-08-28 02:53:08 +03:00
|
|
|
}
|
|
|
|
|
2024-03-14 05:52:52 +03:00
|
|
|
// LuaAction makes an action from a lua function. It returns either a BufKeyAction
|
|
|
|
// or a BufMouseAction depending on the event type.
|
|
|
|
func LuaAction(fn string, k Event) BufAction {
|
2019-08-04 01:49:05 +03:00
|
|
|
luaFn := strings.Split(fn, ".")
|
2019-08-26 21:47:27 +03:00
|
|
|
if len(luaFn) <= 1 {
|
|
|
|
return nil
|
|
|
|
}
|
2019-08-04 01:49:05 +03:00
|
|
|
plName, plFn := luaFn[0], luaFn[1]
|
|
|
|
pl := config.FindPlugin(plName)
|
2019-08-26 21:47:27 +03:00
|
|
|
if pl == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2020-11-05 23:52:25 +03:00
|
|
|
|
2024-03-14 05:52:52 +03:00
|
|
|
var action BufAction
|
|
|
|
switch k.(type) {
|
2020-07-02 00:11:07 +03:00
|
|
|
case KeyEvent, KeySequenceEvent, RawEvent:
|
2024-03-14 05:52:52 +03:00
|
|
|
action = BufKeyAction(func(h *BufPane) bool {
|
|
|
|
val, err := pl.Call(plFn, luar.New(ulua.L, h))
|
|
|
|
if err != nil {
|
|
|
|
screen.TermMessage(err)
|
|
|
|
}
|
|
|
|
if v, ok := val.(lua.LBool); !ok {
|
|
|
|
return false
|
|
|
|
} else {
|
|
|
|
return bool(v)
|
|
|
|
}
|
|
|
|
})
|
2020-07-02 00:11:07 +03:00
|
|
|
case MouseEvent:
|
2024-03-14 05:52:52 +03:00
|
|
|
action = BufMouseAction(func(h *BufPane, te *tcell.EventMouse) bool {
|
|
|
|
val, err := pl.Call(plFn, luar.New(ulua.L, h), luar.New(ulua.L, te))
|
|
|
|
if err != nil {
|
|
|
|
screen.TermMessage(err)
|
|
|
|
}
|
|
|
|
if v, ok := val.(lua.LBool); !ok {
|
|
|
|
return false
|
|
|
|
} else {
|
|
|
|
return bool(v)
|
|
|
|
}
|
|
|
|
})
|
2020-07-02 00:11:07 +03:00
|
|
|
}
|
2024-03-14 05:52:52 +03:00
|
|
|
return action
|
2020-07-02 00:11:07 +03:00
|
|
|
}
|
|
|
|
|
2024-03-14 05:52:52 +03:00
|
|
|
// BufMapEvent maps an event to an action
|
|
|
|
func BufMapEvent(k Event, action string) {
|
|
|
|
config.Bindings["buffer"][k.Name()] = action
|
|
|
|
|
|
|
|
var actionfns []BufAction
|
2019-12-25 20:54:51 +03:00
|
|
|
var names []string
|
|
|
|
var types []byte
|
|
|
|
for i := 0; ; i++ {
|
|
|
|
if action == "" {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
2019-12-27 08:43:45 +03:00
|
|
|
// TODO: fix problem when complex bindings have these
|
|
|
|
// characters (escape them?)
|
2019-12-25 20:54:51 +03:00
|
|
|
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 = ""
|
|
|
|
}
|
|
|
|
|
2024-03-14 05:52:52 +03:00
|
|
|
var afn BufAction
|
2019-12-27 08:43:45 +03:00
|
|
|
if strings.HasPrefix(a, "command:") {
|
2019-08-17 21:49:42 +03:00
|
|
|
a = strings.SplitN(a, ":", 2)[1]
|
|
|
|
afn = CommandAction(a)
|
2019-12-25 20:54:51 +03:00
|
|
|
names = append(names, "")
|
2019-08-17 21:49:42 +03:00
|
|
|
} else if strings.HasPrefix(a, "command-edit:") {
|
|
|
|
a = strings.SplitN(a, ":", 2)[1]
|
|
|
|
afn = CommandEditAction(a)
|
2019-12-25 20:54:51 +03:00
|
|
|
names = append(names, "")
|
2019-08-17 21:49:42 +03:00
|
|
|
} else if strings.HasPrefix(a, "lua:") {
|
|
|
|
a = strings.SplitN(a, ":", 2)[1]
|
2024-03-14 05:52:52 +03:00
|
|
|
afn = LuaAction(a, k)
|
2019-12-25 01:17:44 +03:00
|
|
|
if afn == nil {
|
2019-12-27 08:43:45 +03:00
|
|
|
screen.TermMessage("Lua Error:", a, "does not exist")
|
2019-12-25 01:17:44 +03:00
|
|
|
continue
|
|
|
|
}
|
2020-02-06 19:12:34 +03:00
|
|
|
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)
|
2019-08-17 21:49:42 +03:00
|
|
|
} else if f, ok := BufKeyActions[a]; ok {
|
|
|
|
afn = f
|
2019-12-25 20:54:51 +03:00
|
|
|
names = append(names, a)
|
2024-03-14 05:52:52 +03:00
|
|
|
} else if f, ok := BufMouseActions[a]; ok {
|
|
|
|
afn = f
|
|
|
|
names = append(names, a)
|
2019-08-17 21:49:42 +03:00
|
|
|
} else {
|
2020-04-26 00:01:16 +03:00
|
|
|
screen.TermMessage("Error in bindings: action", a, "does not exist")
|
2019-12-25 20:54:51 +03:00
|
|
|
continue
|
2019-08-17 21:49:42 +03:00
|
|
|
}
|
2019-12-25 20:54:51 +03:00
|
|
|
actionfns = append(actionfns, afn)
|
2019-08-17 21:49:42 +03:00
|
|
|
}
|
2024-03-14 05:52:52 +03:00
|
|
|
bufAction := func(h *BufPane, te *tcell.EventMouse) bool {
|
2019-12-25 20:54:51 +03:00
|
|
|
cursors := h.Buf.GetCursors()
|
|
|
|
success := true
|
|
|
|
for i, a := range actionfns {
|
2020-07-05 04:26:36 +03:00
|
|
|
innerSuccess := true
|
2019-12-25 20:54:51 +03:00
|
|
|
for j, c := range cursors {
|
2020-02-12 21:05:15 +03:00
|
|
|
if c == nil {
|
|
|
|
continue
|
|
|
|
}
|
2019-12-25 20:54:51 +03:00
|
|
|
h.Buf.SetCurCursor(c.Num)
|
|
|
|
h.Cursor = c
|
|
|
|
if i == 0 || (success && types[i-1] == '&') || (!success && types[i-1] == '|') || (types[i-1] == ',') {
|
2024-03-14 05:52:52 +03:00
|
|
|
innerSuccess = innerSuccess && h.execAction(a, names[i], j, te)
|
2019-12-25 20:54:51 +03:00
|
|
|
} else {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2020-05-29 21:38:29 +03:00
|
|
|
// if the action changed the current pane, update the reference
|
|
|
|
h = MainTab().CurPane()
|
2020-07-05 04:26:36 +03:00
|
|
|
success = innerSuccess
|
2019-08-17 21:49:42 +03:00
|
|
|
}
|
2019-12-25 20:54:51 +03:00
|
|
|
return true
|
2018-08-29 06:30:39 +03:00
|
|
|
}
|
2020-07-01 04:25:54 +03:00
|
|
|
|
2024-03-14 05:52:52 +03:00
|
|
|
switch e := k.(type) {
|
|
|
|
case KeyEvent, KeySequenceEvent, RawEvent:
|
|
|
|
BufBindings.RegisterKeyBinding(e, BufKeyActionGeneral(func(h *BufPane) bool {
|
|
|
|
return bufAction(h, nil)
|
|
|
|
}))
|
|
|
|
case MouseEvent:
|
|
|
|
BufBindings.RegisterMouseBinding(e, BufMouseActionGeneral(bufAction))
|
2018-08-29 06:30:39 +03:00
|
|
|
}
|
2018-08-28 02:53:08 +03:00
|
|
|
}
|
|
|
|
|
2020-05-15 04:50:28 +03:00
|
|
|
// BufUnmap unmaps a key or mouse event from any action
|
|
|
|
func BufUnmap(k Event) {
|
2020-07-01 04:25:54 +03:00
|
|
|
// TODO
|
|
|
|
// delete(BufKeyBindings, k)
|
|
|
|
//
|
|
|
|
// switch e := k.(type) {
|
|
|
|
// case MouseEvent:
|
|
|
|
// delete(BufMouseBindings, e)
|
|
|
|
// }
|
2020-05-15 04:50:28 +03:00
|
|
|
}
|
|
|
|
|
2019-01-19 23:37:59 +03:00
|
|
|
// The BufPane connects the buffer and the window
|
2018-08-28 02:53:08 +03:00
|
|
|
// It provides a cursor (or multiple) and defines a set of actions
|
|
|
|
// that can be taken on the buffer
|
|
|
|
// The ActionHandler can access the window for necessary info about
|
|
|
|
// visual positions for mouse clicks and scrolling
|
2019-01-19 23:37:59 +03:00
|
|
|
type BufPane struct {
|
2019-01-14 08:57:39 +03:00
|
|
|
display.BWindow
|
2019-01-11 05:26:58 +03:00
|
|
|
|
2020-07-01 04:25:54 +03:00
|
|
|
// Buf is the buffer this BufPane views
|
2018-08-28 02:53:08 +03:00
|
|
|
Buf *buffer.Buffer
|
2020-07-01 04:25:54 +03:00
|
|
|
// Bindings stores the association of key events and actions
|
2020-07-01 05:51:13 +03:00
|
|
|
bindings *KeyTree
|
2018-08-28 02:53:08 +03:00
|
|
|
|
2020-07-01 04:25:54 +03:00
|
|
|
// Cursor is the currently active buffer cursor
|
|
|
|
Cursor *buffer.Cursor
|
2018-08-28 02:53:08 +03:00
|
|
|
|
2022-10-26 00:31:50 +03:00
|
|
|
// Since tcell doesn't differentiate between a mouse press event
|
|
|
|
// and a mouse move event with button pressed (nor between a mouse
|
|
|
|
// release event and a mouse move event with no buttons pressed),
|
|
|
|
// we need to keep track of whether or not the mouse was previously
|
|
|
|
// pressed, to determine mouse release and mouse drag events.
|
|
|
|
// Moreover, since in case of a release event tcell doesn't tell us
|
|
|
|
// which button was released, we need to keep track of which
|
|
|
|
// (possibly multiple) buttons were pressed previously.
|
|
|
|
mousePressed map[MouseEvent]bool
|
2018-08-28 02:53:08 +03:00
|
|
|
|
|
|
|
// We need to keep track of insert key press toggle
|
|
|
|
isOverwriteMode bool
|
|
|
|
// This stores when the last click was
|
|
|
|
// This is useful for detecting double and triple clicks
|
|
|
|
lastClickTime time.Time
|
|
|
|
lastLoc buffer.Loc
|
|
|
|
|
|
|
|
// lastCutTime stores when the last ctrl+k was issued.
|
|
|
|
// It is used for clearing the clipboard to replace it with fresh cut lines.
|
|
|
|
lastCutTime time.Time
|
|
|
|
|
|
|
|
// freshClip returns true if the clipboard has never been pasted.
|
|
|
|
freshClip bool
|
|
|
|
|
|
|
|
// Was the last mouse event actually a double click?
|
|
|
|
// Useful for detecting triple clicks -- if a double click is detected
|
|
|
|
// but the last mouse event was actually a double click, it's a triple click
|
|
|
|
doubleClick bool
|
|
|
|
// Same here, just to keep track for mouse move events
|
|
|
|
tripleClick bool
|
2019-01-03 23:35:24 +03:00
|
|
|
|
2019-01-05 05:48:19 +03:00
|
|
|
// Should the current multiple cursor selection search based on word or
|
|
|
|
// based on selection (false for selection, true for word)
|
|
|
|
multiWord bool
|
2019-01-05 01:40:56 +03:00
|
|
|
|
|
|
|
splitID uint64
|
2020-02-06 01:16:31 +03:00
|
|
|
tab *Tab
|
2019-06-15 21:44:03 +03:00
|
|
|
|
|
|
|
// remember original location of a search in case the search is canceled
|
|
|
|
searchOrig buffer.Loc
|
Improve buffer view relocation after jumping to a far-away location (#2628)
* Improve buffer view relocation after jumping to a far-away location
When the cursor is moved to a location which is far away from the
current location (e.g. after a search or a goto line), the buffer view
is always relocated in such a way that the cursor is at the bottom or
at the top (minus scrollmargin), i.e. as if we just scrolled to this
location. It's not like in other editors, and IMHO it's annoying. When
we jump to a new location far away, we usually want to see more of its
context, so the cursor should be placed closer to the center of the
view, not near its edges.
This change implements the behavior similar to other editors:
- If the distance between the new and the old location is less than one
frame (i.e. the view either doesn't change or just slightly "shifts")
then the current behavior remains unchanged.
- Otherwise the current line is placed at 25% of the window height.
* Postpone calling onBufPaneOpen until the initial resize
It is currently not possible to find out the geometry of a newly created
bufpane in onBufPaneOpen lua callback: bp:GetView() returns {0,0,0,0}
instead of the actual window. The reason is that the bufpane view is not
properly initialized yet when the bufpane is created and the callback is
triggered. It is initialized a bit later, at the initial resize.
So postpone calling onBufPaneOpen until after the initial resize.
* Improve buffer view relocation when opening a file at a far-away location
When a file is opened with the initial cursor location at a given line
which is far away from the beginning of the file, the buffer view is
relocated so that the cursor is at the bottom (minus scrollmargin)
as if we just scrolled to this line, which is annoying since we'd rather
like to see more of the context of this initial location.
So implement the behavior similar to the earlier commit (which addresses
a similar issue about jumping far away after a search or goto):
- If the initial cursor location is less than one frame away from the
beginning of the buffer, keep the existing behavior i.e. just display
the beginning of the buffer.
- Otherwise place the cursor location at 25% of the window height.
2022-12-03 06:38:09 +03:00
|
|
|
|
|
|
|
// The pane may not yet be fully initialized after its creation
|
|
|
|
// since we may not know the window geometry yet. In such case we finish
|
|
|
|
// its initialization a bit later, after the initial resize.
|
|
|
|
initialized bool
|
2018-08-28 02:53:08 +03:00
|
|
|
}
|
|
|
|
|
Improve buffer view relocation after jumping to a far-away location (#2628)
* Improve buffer view relocation after jumping to a far-away location
When the cursor is moved to a location which is far away from the
current location (e.g. after a search or a goto line), the buffer view
is always relocated in such a way that the cursor is at the bottom or
at the top (minus scrollmargin), i.e. as if we just scrolled to this
location. It's not like in other editors, and IMHO it's annoying. When
we jump to a new location far away, we usually want to see more of its
context, so the cursor should be placed closer to the center of the
view, not near its edges.
This change implements the behavior similar to other editors:
- If the distance between the new and the old location is less than one
frame (i.e. the view either doesn't change or just slightly "shifts")
then the current behavior remains unchanged.
- Otherwise the current line is placed at 25% of the window height.
* Postpone calling onBufPaneOpen until the initial resize
It is currently not possible to find out the geometry of a newly created
bufpane in onBufPaneOpen lua callback: bp:GetView() returns {0,0,0,0}
instead of the actual window. The reason is that the bufpane view is not
properly initialized yet when the bufpane is created and the callback is
triggered. It is initialized a bit later, at the initial resize.
So postpone calling onBufPaneOpen until after the initial resize.
* Improve buffer view relocation when opening a file at a far-away location
When a file is opened with the initial cursor location at a given line
which is far away from the beginning of the file, the buffer view is
relocated so that the cursor is at the bottom (minus scrollmargin)
as if we just scrolled to this line, which is annoying since we'd rather
like to see more of the context of this initial location.
So implement the behavior similar to the earlier commit (which addresses
a similar issue about jumping far away after a search or goto):
- If the initial cursor location is less than one frame away from the
beginning of the buffer, keep the existing behavior i.e. just display
the beginning of the buffer.
- Otherwise place the cursor location at 25% of the window height.
2022-12-03 06:38:09 +03:00
|
|
|
func newBufPane(buf *buffer.Buffer, win display.BWindow, tab *Tab) *BufPane {
|
2019-01-19 23:37:59 +03:00
|
|
|
h := new(BufPane)
|
2018-08-29 01:44:52 +03:00
|
|
|
h.Buf = buf
|
2019-01-14 08:57:39 +03:00
|
|
|
h.BWindow = win
|
2020-02-06 01:16:31 +03:00
|
|
|
h.tab = tab
|
2018-08-28 02:53:08 +03:00
|
|
|
|
2019-01-14 08:18:49 +03:00
|
|
|
h.Cursor = h.Buf.GetActiveCursor()
|
2022-10-26 00:31:50 +03:00
|
|
|
h.mousePressed = make(map[MouseEvent]bool)
|
2018-08-28 02:53:08 +03:00
|
|
|
|
Improve buffer view relocation after jumping to a far-away location (#2628)
* Improve buffer view relocation after jumping to a far-away location
When the cursor is moved to a location which is far away from the
current location (e.g. after a search or a goto line), the buffer view
is always relocated in such a way that the cursor is at the bottom or
at the top (minus scrollmargin), i.e. as if we just scrolled to this
location. It's not like in other editors, and IMHO it's annoying. When
we jump to a new location far away, we usually want to see more of its
context, so the cursor should be placed closer to the center of the
view, not near its edges.
This change implements the behavior similar to other editors:
- If the distance between the new and the old location is less than one
frame (i.e. the view either doesn't change or just slightly "shifts")
then the current behavior remains unchanged.
- Otherwise the current line is placed at 25% of the window height.
* Postpone calling onBufPaneOpen until the initial resize
It is currently not possible to find out the geometry of a newly created
bufpane in onBufPaneOpen lua callback: bp:GetView() returns {0,0,0,0}
instead of the actual window. The reason is that the bufpane view is not
properly initialized yet when the bufpane is created and the callback is
triggered. It is initialized a bit later, at the initial resize.
So postpone calling onBufPaneOpen until after the initial resize.
* Improve buffer view relocation when opening a file at a far-away location
When a file is opened with the initial cursor location at a given line
which is far away from the beginning of the file, the buffer view is
relocated so that the cursor is at the bottom (minus scrollmargin)
as if we just scrolled to this line, which is annoying since we'd rather
like to see more of the context of this initial location.
So implement the behavior similar to the earlier commit (which addresses
a similar issue about jumping far away after a search or goto):
- If the initial cursor location is less than one frame away from the
beginning of the buffer, keep the existing behavior i.e. just display
the beginning of the buffer.
- Otherwise place the cursor location at 25% of the window height.
2022-12-03 06:38:09 +03:00
|
|
|
return h
|
|
|
|
}
|
2019-03-20 01:28:51 +03:00
|
|
|
|
Improve buffer view relocation after jumping to a far-away location (#2628)
* Improve buffer view relocation after jumping to a far-away location
When the cursor is moved to a location which is far away from the
current location (e.g. after a search or a goto line), the buffer view
is always relocated in such a way that the cursor is at the bottom or
at the top (minus scrollmargin), i.e. as if we just scrolled to this
location. It's not like in other editors, and IMHO it's annoying. When
we jump to a new location far away, we usually want to see more of its
context, so the cursor should be placed closer to the center of the
view, not near its edges.
This change implements the behavior similar to other editors:
- If the distance between the new and the old location is less than one
frame (i.e. the view either doesn't change or just slightly "shifts")
then the current behavior remains unchanged.
- Otherwise the current line is placed at 25% of the window height.
* Postpone calling onBufPaneOpen until the initial resize
It is currently not possible to find out the geometry of a newly created
bufpane in onBufPaneOpen lua callback: bp:GetView() returns {0,0,0,0}
instead of the actual window. The reason is that the bufpane view is not
properly initialized yet when the bufpane is created and the callback is
triggered. It is initialized a bit later, at the initial resize.
So postpone calling onBufPaneOpen until after the initial resize.
* Improve buffer view relocation when opening a file at a far-away location
When a file is opened with the initial cursor location at a given line
which is far away from the beginning of the file, the buffer view is
relocated so that the cursor is at the bottom (minus scrollmargin)
as if we just scrolled to this line, which is annoying since we'd rather
like to see more of the context of this initial location.
So implement the behavior similar to the earlier commit (which addresses
a similar issue about jumping far away after a search or goto):
- If the initial cursor location is less than one frame away from the
beginning of the buffer, keep the existing behavior i.e. just display
the beginning of the buffer.
- Otherwise place the cursor location at 25% of the window height.
2022-12-03 06:38:09 +03:00
|
|
|
// NewBufPane creates a new buffer pane with the given window.
|
|
|
|
func NewBufPane(buf *buffer.Buffer, win display.BWindow, tab *Tab) *BufPane {
|
|
|
|
h := newBufPane(buf, win, tab)
|
|
|
|
h.finishInitialize()
|
2018-08-29 01:44:52 +03:00
|
|
|
return h
|
2018-08-28 02:53:08 +03:00
|
|
|
}
|
|
|
|
|
2021-08-22 00:58:30 +03:00
|
|
|
// NewBufPaneFromBuf constructs a new pane from the given buffer and automatically
|
|
|
|
// creates a buf window.
|
2020-02-06 01:16:31 +03:00
|
|
|
func NewBufPaneFromBuf(buf *buffer.Buffer, tab *Tab) *BufPane {
|
2019-01-19 23:37:59 +03:00
|
|
|
w := display.NewBufWindow(0, 0, 0, 0, buf)
|
Improve buffer view relocation after jumping to a far-away location (#2628)
* Improve buffer view relocation after jumping to a far-away location
When the cursor is moved to a location which is far away from the
current location (e.g. after a search or a goto line), the buffer view
is always relocated in such a way that the cursor is at the bottom or
at the top (minus scrollmargin), i.e. as if we just scrolled to this
location. It's not like in other editors, and IMHO it's annoying. When
we jump to a new location far away, we usually want to see more of its
context, so the cursor should be placed closer to the center of the
view, not near its edges.
This change implements the behavior similar to other editors:
- If the distance between the new and the old location is less than one
frame (i.e. the view either doesn't change or just slightly "shifts")
then the current behavior remains unchanged.
- Otherwise the current line is placed at 25% of the window height.
* Postpone calling onBufPaneOpen until the initial resize
It is currently not possible to find out the geometry of a newly created
bufpane in onBufPaneOpen lua callback: bp:GetView() returns {0,0,0,0}
instead of the actual window. The reason is that the bufpane view is not
properly initialized yet when the bufpane is created and the callback is
triggered. It is initialized a bit later, at the initial resize.
So postpone calling onBufPaneOpen until after the initial resize.
* Improve buffer view relocation when opening a file at a far-away location
When a file is opened with the initial cursor location at a given line
which is far away from the beginning of the file, the buffer view is
relocated so that the cursor is at the bottom (minus scrollmargin)
as if we just scrolled to this line, which is annoying since we'd rather
like to see more of the context of this initial location.
So implement the behavior similar to the earlier commit (which addresses
a similar issue about jumping far away after a search or goto):
- If the initial cursor location is less than one frame away from the
beginning of the buffer, keep the existing behavior i.e. just display
the beginning of the buffer.
- Otherwise place the cursor location at 25% of the window height.
2022-12-03 06:38:09 +03:00
|
|
|
h := newBufPane(buf, w, tab)
|
|
|
|
// Postpone finishing initializing the pane until we know the actual geometry
|
|
|
|
// of the buf window.
|
|
|
|
return h
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: make sure splitID and tab are set before finishInitialize is called
|
|
|
|
func (h *BufPane) finishInitialize() {
|
|
|
|
h.initialRelocate()
|
|
|
|
h.initialized = true
|
|
|
|
config.RunPluginFn("onBufPaneOpen", luar.New(ulua.L, h))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Resize resizes the pane
|
|
|
|
func (h *BufPane) Resize(width, height int) {
|
|
|
|
h.BWindow.Resize(width, height)
|
|
|
|
if !h.initialized {
|
|
|
|
h.finishInitialize()
|
|
|
|
}
|
2020-02-06 01:16:31 +03:00
|
|
|
}
|
|
|
|
|
2021-08-22 00:58:30 +03:00
|
|
|
// SetTab sets this pane's tab.
|
2020-02-06 01:16:31 +03:00
|
|
|
func (h *BufPane) SetTab(t *Tab) {
|
|
|
|
h.tab = t
|
|
|
|
}
|
|
|
|
|
2021-08-22 00:58:30 +03:00
|
|
|
// Tab returns this pane's tab.
|
2020-02-06 01:16:31 +03:00
|
|
|
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()
|
2019-01-19 23:37:59 +03:00
|
|
|
}
|
|
|
|
|
2021-08-22 00:58:30 +03:00
|
|
|
// PluginCB calls all plugin callbacks with a certain name and displays an
|
2024-04-08 13:04:38 +03:00
|
|
|
// error if there is one and returns the aggregate boolean response
|
2019-08-03 00:48:59 +03:00
|
|
|
func (h *BufPane) PluginCB(cb string) bool {
|
2023-06-06 03:38:33 +03:00
|
|
|
b, err := config.RunPluginFnBool(h.Buf.Settings, cb, luar.New(ulua.L, h))
|
2019-08-03 00:48:59 +03:00
|
|
|
if err != nil {
|
|
|
|
screen.TermMessage(err)
|
|
|
|
}
|
|
|
|
return b
|
|
|
|
}
|
|
|
|
|
2021-08-22 00:58:30 +03:00
|
|
|
// PluginCBRune is the same as PluginCB but also passes a rune to the plugins
|
2019-08-03 00:48:59 +03:00
|
|
|
func (h *BufPane) PluginCBRune(cb string, r rune) bool {
|
2023-06-06 03:38:33 +03:00
|
|
|
b, err := config.RunPluginFnBool(h.Buf.Settings, cb, luar.New(ulua.L, h), luar.New(ulua.L, string(r)))
|
2019-08-03 00:48:59 +03:00
|
|
|
if err != nil {
|
|
|
|
screen.TermMessage(err)
|
|
|
|
}
|
|
|
|
return b
|
|
|
|
}
|
|
|
|
|
2022-11-05 13:41:04 +03:00
|
|
|
func (h *BufPane) resetMouse() {
|
|
|
|
for me := range h.mousePressed {
|
|
|
|
delete(h.mousePressed, me)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-22 00:58:30 +03:00
|
|
|
// OpenBuffer opens the given buffer in this pane.
|
2019-01-19 23:37:59 +03:00
|
|
|
func (h *BufPane) OpenBuffer(b *buffer.Buffer) {
|
2019-01-14 08:57:39 +03:00
|
|
|
h.Buf.Close()
|
|
|
|
h.Buf = b
|
|
|
|
h.BWindow.SetBuffer(b)
|
|
|
|
h.Cursor = b.GetActiveCursor()
|
2019-06-16 01:00:24 +03:00
|
|
|
h.Resize(h.GetView().Width, h.GetView().Height)
|
Improve buffer view relocation after jumping to a far-away location (#2628)
* Improve buffer view relocation after jumping to a far-away location
When the cursor is moved to a location which is far away from the
current location (e.g. after a search or a goto line), the buffer view
is always relocated in such a way that the cursor is at the bottom or
at the top (minus scrollmargin), i.e. as if we just scrolled to this
location. It's not like in other editors, and IMHO it's annoying. When
we jump to a new location far away, we usually want to see more of its
context, so the cursor should be placed closer to the center of the
view, not near its edges.
This change implements the behavior similar to other editors:
- If the distance between the new and the old location is less than one
frame (i.e. the view either doesn't change or just slightly "shifts")
then the current behavior remains unchanged.
- Otherwise the current line is placed at 25% of the window height.
* Postpone calling onBufPaneOpen until the initial resize
It is currently not possible to find out the geometry of a newly created
bufpane in onBufPaneOpen lua callback: bp:GetView() returns {0,0,0,0}
instead of the actual window. The reason is that the bufpane view is not
properly initialized yet when the bufpane is created and the callback is
triggered. It is initialized a bit later, at the initial resize.
So postpone calling onBufPaneOpen until after the initial resize.
* Improve buffer view relocation when opening a file at a far-away location
When a file is opened with the initial cursor location at a given line
which is far away from the beginning of the file, the buffer view is
relocated so that the cursor is at the bottom (minus scrollmargin)
as if we just scrolled to this line, which is annoying since we'd rather
like to see more of the context of this initial location.
So implement the behavior similar to the earlier commit (which addresses
a similar issue about jumping far away after a search or goto):
- If the initial cursor location is less than one frame away from the
beginning of the buffer, keep the existing behavior i.e. just display
the beginning of the buffer.
- Otherwise place the cursor location at 25% of the window height.
2022-12-03 06:38:09 +03:00
|
|
|
h.initialRelocate()
|
2021-08-22 00:58:30 +03:00
|
|
|
// Set mouseReleased to true because we assume the mouse is not being
|
|
|
|
// pressed when the editor is opened
|
2022-11-05 13:41:04 +03:00
|
|
|
h.resetMouse()
|
2021-08-22 00:58:30 +03:00
|
|
|
// Set isOverwriteMode to false, because we assume we are in the default
|
|
|
|
// mode when editor is opened
|
2019-01-14 08:57:39 +03:00
|
|
|
h.isOverwriteMode = false
|
|
|
|
h.lastClickTime = time.Time{}
|
|
|
|
}
|
|
|
|
|
Improve buffer view relocation after jumping to a far-away location (#2628)
* Improve buffer view relocation after jumping to a far-away location
When the cursor is moved to a location which is far away from the
current location (e.g. after a search or a goto line), the buffer view
is always relocated in such a way that the cursor is at the bottom or
at the top (minus scrollmargin), i.e. as if we just scrolled to this
location. It's not like in other editors, and IMHO it's annoying. When
we jump to a new location far away, we usually want to see more of its
context, so the cursor should be placed closer to the center of the
view, not near its edges.
This change implements the behavior similar to other editors:
- If the distance between the new and the old location is less than one
frame (i.e. the view either doesn't change or just slightly "shifts")
then the current behavior remains unchanged.
- Otherwise the current line is placed at 25% of the window height.
* Postpone calling onBufPaneOpen until the initial resize
It is currently not possible to find out the geometry of a newly created
bufpane in onBufPaneOpen lua callback: bp:GetView() returns {0,0,0,0}
instead of the actual window. The reason is that the bufpane view is not
properly initialized yet when the bufpane is created and the callback is
triggered. It is initialized a bit later, at the initial resize.
So postpone calling onBufPaneOpen until after the initial resize.
* Improve buffer view relocation when opening a file at a far-away location
When a file is opened with the initial cursor location at a given line
which is far away from the beginning of the file, the buffer view is
relocated so that the cursor is at the bottom (minus scrollmargin)
as if we just scrolled to this line, which is annoying since we'd rather
like to see more of the context of this initial location.
So implement the behavior similar to the earlier commit (which addresses
a similar issue about jumping far away after a search or goto):
- If the initial cursor location is less than one frame away from the
beginning of the buffer, keep the existing behavior i.e. just display
the beginning of the buffer.
- Otherwise place the cursor location at 25% of the window height.
2022-12-03 06:38:09 +03:00
|
|
|
// GotoLoc moves the cursor to a new location and adjusts the view accordingly.
|
|
|
|
// Use GotoLoc when the new location may be far away from the current location.
|
|
|
|
func (h *BufPane) GotoLoc(loc buffer.Loc) {
|
|
|
|
sloc := h.SLocFromLoc(loc)
|
|
|
|
d := h.Diff(h.SLocFromLoc(h.Cursor.Loc), sloc)
|
|
|
|
|
|
|
|
h.Cursor.GotoLoc(loc)
|
|
|
|
|
|
|
|
// If the new location is far away from the previous one,
|
|
|
|
// ensure the cursor is at 25% of the window height
|
|
|
|
height := h.BufView().Height
|
|
|
|
if util.Abs(d) >= height {
|
|
|
|
v := h.GetView()
|
|
|
|
v.StartLine = h.Scroll(sloc, -height/4)
|
|
|
|
h.ScrollAdjust()
|
|
|
|
v.StartCol = 0
|
|
|
|
}
|
|
|
|
h.Relocate()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *BufPane) initialRelocate() {
|
|
|
|
sloc := h.SLocFromLoc(h.Cursor.Loc)
|
|
|
|
height := h.BufView().Height
|
|
|
|
|
|
|
|
// If the initial cursor location is far away from the beginning
|
|
|
|
// of the buffer, ensure the cursor is at 25% of the window height
|
|
|
|
v := h.GetView()
|
|
|
|
if h.Diff(display.SLoc{0, 0}, sloc) < height {
|
|
|
|
v.StartLine = display.SLoc{0, 0}
|
|
|
|
} else {
|
|
|
|
v.StartLine = h.Scroll(sloc, -height/4)
|
|
|
|
h.ScrollAdjust()
|
|
|
|
}
|
|
|
|
v.StartCol = 0
|
|
|
|
h.Relocate()
|
|
|
|
}
|
|
|
|
|
2021-08-22 00:58:30 +03:00
|
|
|
// ID returns this pane's split id.
|
2019-01-19 23:37:59 +03:00
|
|
|
func (h *BufPane) ID() uint64 {
|
2019-01-11 22:49:22 +03:00
|
|
|
return h.splitID
|
|
|
|
}
|
|
|
|
|
2021-08-22 00:58:30 +03:00
|
|
|
// SetID sets the split ID of this pane.
|
2019-01-19 23:37:59 +03:00
|
|
|
func (h *BufPane) SetID(i uint64) {
|
|
|
|
h.splitID = i
|
|
|
|
}
|
|
|
|
|
2021-08-22 00:58:30 +03:00
|
|
|
// Name returns the BufPane's name.
|
2019-01-19 23:37:59 +03:00
|
|
|
func (h *BufPane) Name() string {
|
2020-02-24 21:45:10 +03:00
|
|
|
n := h.Buf.GetName()
|
|
|
|
if h.Buf.Modified() {
|
|
|
|
n += " +"
|
|
|
|
}
|
|
|
|
return n
|
2019-01-11 22:49:22 +03:00
|
|
|
}
|
|
|
|
|
2023-10-22 01:18:41 +03:00
|
|
|
func (h *BufPane) getReloadSetting() string {
|
|
|
|
reloadSetting := h.Buf.Settings["reload"]
|
|
|
|
return reloadSetting.(string)
|
|
|
|
}
|
|
|
|
|
2018-08-28 02:53:08 +03:00
|
|
|
// HandleEvent executes the tcell event properly
|
2019-01-19 23:37:59 +03:00
|
|
|
func (h *BufPane) HandleEvent(event tcell.Event) {
|
2020-02-12 21:56:00 +03:00
|
|
|
if h.Buf.ExternallyModified() && !h.Buf.ReloadDisabled {
|
2023-10-22 01:18:41 +03:00
|
|
|
reload := h.getReloadSetting()
|
2019-12-27 01:59:23 +03:00
|
|
|
|
2023-10-22 01:18:41 +03:00
|
|
|
if reload == "prompt" {
|
|
|
|
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()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
} else if reload == "auto" {
|
|
|
|
h.Buf.ReOpen()
|
|
|
|
} else if reload == "disabled" {
|
|
|
|
h.Buf.DisableReload()
|
|
|
|
} else {
|
|
|
|
InfoBar.Message("Invalid reload setting")
|
|
|
|
}
|
2019-12-27 01:59:23 +03:00
|
|
|
}
|
|
|
|
|
2018-08-28 02:53:08 +03:00
|
|
|
switch e := event.(type) {
|
2020-01-01 06:34:43 +03:00
|
|
|
case *tcell.EventRaw:
|
|
|
|
re := RawEvent{
|
|
|
|
esc: e.EscSeq(),
|
|
|
|
}
|
|
|
|
h.DoKeyEvent(re)
|
2020-01-01 04:15:45 +03:00
|
|
|
case *tcell.EventPaste:
|
|
|
|
h.paste(e.Text())
|
|
|
|
h.Relocate()
|
2018-08-28 02:53:08 +03:00
|
|
|
case *tcell.EventKey:
|
|
|
|
ke := KeyEvent{
|
|
|
|
code: e.Key(),
|
2020-09-06 04:59:19 +03:00
|
|
|
mod: metaToAlt(e.Modifiers()),
|
2018-08-28 02:53:08 +03:00
|
|
|
r: e.Rune(),
|
|
|
|
}
|
2019-01-03 07:26:40 +03:00
|
|
|
|
|
|
|
done := h.DoKeyEvent(ke)
|
|
|
|
if !done && e.Key() == tcell.KeyRune {
|
|
|
|
h.DoRuneInsert(e.Rune())
|
2018-08-29 06:30:39 +03:00
|
|
|
}
|
2018-08-28 02:53:08 +03:00
|
|
|
case *tcell.EventMouse:
|
2022-10-26 00:31:50 +03:00
|
|
|
if e.Buttons() != tcell.ButtonNone {
|
|
|
|
me := MouseEvent{
|
|
|
|
btn: e.Buttons(),
|
|
|
|
mod: metaToAlt(e.Modifiers()),
|
|
|
|
state: MousePress,
|
2019-12-28 23:56:56 +03:00
|
|
|
}
|
2022-11-03 02:14:26 +03:00
|
|
|
isDrag := len(h.mousePressed) > 0
|
2019-01-03 01:39:50 +03:00
|
|
|
|
2022-10-26 00:31:50 +03:00
|
|
|
if e.Buttons() & ^(tcell.WheelUp|tcell.WheelDown|tcell.WheelLeft|tcell.WheelRight) != tcell.ButtonNone {
|
|
|
|
h.mousePressed[me] = true
|
2019-01-03 01:39:50 +03:00
|
|
|
}
|
|
|
|
|
2022-11-03 02:14:26 +03:00
|
|
|
if isDrag {
|
|
|
|
me.state = MouseDrag
|
2019-12-28 23:56:56 +03:00
|
|
|
}
|
|
|
|
h.DoMouseEvent(me, e)
|
2022-10-26 00:31:50 +03:00
|
|
|
} else {
|
|
|
|
// Mouse event with no click - mouse was just released.
|
|
|
|
// If there were multiple mouse buttons pressed, we don't know which one
|
|
|
|
// was actually released, so we assume they all were released.
|
|
|
|
for me := range h.mousePressed {
|
|
|
|
delete(h.mousePressed, me)
|
|
|
|
|
|
|
|
me.state = MouseRelease
|
|
|
|
h.DoMouseEvent(me, e)
|
|
|
|
}
|
2018-08-28 02:53:08 +03:00
|
|
|
}
|
2018-08-29 01:44:52 +03:00
|
|
|
}
|
2019-01-05 05:50:11 +03:00
|
|
|
h.Buf.MergeCursors()
|
2019-01-14 01:22:11 +03:00
|
|
|
|
2019-08-04 09:53:33 +03:00
|
|
|
if h.IsActive() {
|
|
|
|
// Display any gutter messages for this line
|
|
|
|
c := h.Buf.GetActiveCursor()
|
|
|
|
none := true
|
|
|
|
for _, m := range h.Buf.Messages {
|
|
|
|
if c.Y == m.Start.Y || c.Y == m.End.Y {
|
|
|
|
InfoBar.GutterMessage(m.Msg)
|
|
|
|
none = false
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if none && InfoBar.HasGutter {
|
|
|
|
InfoBar.ClearGutter()
|
2019-01-14 01:22:11 +03:00
|
|
|
}
|
|
|
|
}
|
2020-10-20 23:52:49 +03:00
|
|
|
|
|
|
|
cursors := h.Buf.GetCursors()
|
|
|
|
for _, c := range cursors {
|
2020-10-22 23:54:46 +03:00
|
|
|
if c.NewTrailingWsY != c.Y && (!c.HasSelection() ||
|
|
|
|
(c.NewTrailingWsY != c.CurSelection[0].Y && c.NewTrailingWsY != c.CurSelection[1].Y)) {
|
2020-10-20 23:52:49 +03:00
|
|
|
c.NewTrailingWsY = -1
|
|
|
|
}
|
|
|
|
}
|
2018-08-29 01:44:52 +03:00
|
|
|
}
|
|
|
|
|
2021-08-22 00:58:30 +03:00
|
|
|
// Bindings returns the current bindings tree for this buffer.
|
2020-07-01 05:51:13 +03:00
|
|
|
func (h *BufPane) Bindings() *KeyTree {
|
|
|
|
if h.bindings != nil {
|
|
|
|
return h.bindings
|
|
|
|
}
|
|
|
|
return BufBindings
|
|
|
|
}
|
|
|
|
|
2019-01-03 23:27:43 +03:00
|
|
|
// DoKeyEvent executes a key event by finding the action it is bound
|
|
|
|
// to and executing it (possibly multiple times for multiple cursors)
|
2019-01-19 23:37:59 +03:00
|
|
|
func (h *BufPane) DoKeyEvent(e Event) bool {
|
2020-07-01 05:51:13 +03:00
|
|
|
binds := h.Bindings()
|
|
|
|
action, more := binds.NextEvent(e, nil)
|
2020-07-01 04:25:54 +03:00
|
|
|
if action != nil && !more {
|
|
|
|
action(h)
|
2020-07-01 05:51:13 +03:00
|
|
|
binds.ResetEvents()
|
2020-07-02 00:11:07 +03:00
|
|
|
return true
|
2020-07-01 04:25:54 +03:00
|
|
|
} else if action == nil && !more {
|
2020-07-01 05:51:13 +03:00
|
|
|
binds.ResetEvents()
|
2019-12-25 20:54:51 +03:00
|
|
|
}
|
2020-07-02 00:11:07 +03:00
|
|
|
return more
|
2019-12-25 20:54:51 +03:00
|
|
|
}
|
|
|
|
|
2024-03-14 05:52:52 +03:00
|
|
|
func (h *BufPane) execAction(action BufAction, name string, cursor int, te *tcell.EventMouse) bool {
|
2020-01-04 23:51:15 +03:00
|
|
|
if name != "Autocomplete" && name != "CycleAutocompleteBack" {
|
2019-12-25 20:54:51 +03:00
|
|
|
h.Buf.HasSuggestions = false
|
|
|
|
}
|
|
|
|
|
|
|
|
_, isMulti := MultiActions[name]
|
|
|
|
if (!isMulti && cursor == 0) || isMulti {
|
|
|
|
if h.PluginCB("pre" + name) {
|
2024-03-14 05:52:52 +03:00
|
|
|
var success bool
|
|
|
|
switch a := action.(type) {
|
|
|
|
case BufKeyAction:
|
|
|
|
success = a(h)
|
|
|
|
case BufMouseAction:
|
|
|
|
success = a(h, te)
|
|
|
|
}
|
2019-12-30 02:23:17 +03:00
|
|
|
success = success && h.PluginCB("on"+name)
|
2019-08-05 06:23:32 +03:00
|
|
|
|
2019-12-25 20:54:51 +03:00
|
|
|
if isMulti {
|
2021-08-22 01:04:08 +03:00
|
|
|
if recordingMacro {
|
2019-12-25 20:54:51 +03:00
|
|
|
if name != "ToggleMacro" && name != "PlayMacro" {
|
|
|
|
curmacro = append(curmacro, action)
|
2019-08-05 06:23:32 +03:00
|
|
|
}
|
2019-01-03 07:26:40 +03:00
|
|
|
}
|
|
|
|
}
|
2019-12-25 20:54:51 +03:00
|
|
|
|
2019-12-30 02:23:17 +03:00
|
|
|
return success
|
2019-01-03 07:26:40 +03:00
|
|
|
}
|
2018-08-29 01:44:52 +03:00
|
|
|
}
|
2019-12-25 20:54:51 +03:00
|
|
|
|
2018-08-29 01:44:52 +03:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2019-12-30 02:23:17 +03:00
|
|
|
func (h *BufPane) completeAction(action string) {
|
|
|
|
h.PluginCB("on" + action)
|
|
|
|
}
|
|
|
|
|
2019-01-19 23:37:59 +03:00
|
|
|
func (h *BufPane) HasKeyEvent(e Event) bool {
|
2020-07-01 04:25:54 +03:00
|
|
|
// TODO
|
|
|
|
return true
|
|
|
|
// _, ok := BufKeyBindings[e]
|
|
|
|
// return ok
|
2019-01-14 01:58:08 +03:00
|
|
|
}
|
|
|
|
|
2019-01-03 23:27:43 +03:00
|
|
|
// DoMouseEvent executes a mouse event by finding the action it is bound
|
|
|
|
// to and executing it
|
2019-01-19 23:37:59 +03:00
|
|
|
func (h *BufPane) DoMouseEvent(e MouseEvent, te *tcell.EventMouse) bool {
|
2020-07-01 05:51:13 +03:00
|
|
|
binds := h.Bindings()
|
|
|
|
action, _ := binds.NextEvent(e, te)
|
2020-07-01 04:25:54 +03:00
|
|
|
if action != nil {
|
2020-08-12 08:18:15 +03:00
|
|
|
action(h)
|
2020-07-01 05:51:13 +03:00
|
|
|
binds.ResetEvents()
|
2018-08-29 01:44:52 +03:00
|
|
|
return true
|
2018-08-28 02:53:08 +03:00
|
|
|
}
|
2020-07-01 04:25:54 +03:00
|
|
|
// TODO
|
2018-08-29 01:44:52 +03:00
|
|
|
return false
|
2020-07-01 04:25:54 +03:00
|
|
|
|
|
|
|
// if action, ok := BufMouseBindings[e]; ok {
|
|
|
|
// if action(h, te) {
|
|
|
|
// h.Relocate()
|
|
|
|
// }
|
|
|
|
// return true
|
|
|
|
// } else if h.HasKeyEvent(e) {
|
|
|
|
// return h.DoKeyEvent(e)
|
|
|
|
// }
|
|
|
|
// return false
|
2018-08-28 02:53:08 +03:00
|
|
|
}
|
|
|
|
|
2019-01-03 23:27:43 +03:00
|
|
|
// DoRuneInsert inserts a given rune into the current buffer
|
|
|
|
// (possibly multiple times for multiple cursors)
|
2019-01-19 23:37:59 +03:00
|
|
|
func (h *BufPane) DoRuneInsert(r rune) {
|
2019-01-03 07:26:40 +03:00
|
|
|
cursors := h.Buf.GetCursors()
|
|
|
|
for _, c := range cursors {
|
|
|
|
// Insert a character
|
2019-01-17 01:52:30 +03:00
|
|
|
h.Buf.SetCurCursor(c.Num)
|
2019-08-05 06:23:32 +03:00
|
|
|
h.Cursor = c
|
2019-08-03 00:48:59 +03:00
|
|
|
if !h.PluginCBRune("preRune", r) {
|
|
|
|
continue
|
|
|
|
}
|
2019-01-03 07:26:40 +03:00
|
|
|
if c.HasSelection() {
|
|
|
|
c.DeleteSelection()
|
|
|
|
c.ResetSelection()
|
|
|
|
}
|
2018-08-29 06:30:39 +03:00
|
|
|
|
2019-01-03 07:26:40 +03:00
|
|
|
if h.isOverwriteMode {
|
|
|
|
next := c.Loc
|
|
|
|
next.X++
|
2019-01-17 06:32:33 +03:00
|
|
|
h.Buf.Replace(c.Loc, next, string(r))
|
2019-01-03 07:26:40 +03:00
|
|
|
} else {
|
2019-01-17 06:32:33 +03:00
|
|
|
h.Buf.Insert(c.Loc, string(r))
|
2019-01-03 07:26:40 +03:00
|
|
|
}
|
2021-08-22 01:04:08 +03:00
|
|
|
if recordingMacro {
|
2019-08-05 06:23:32 +03:00
|
|
|
curmacro = append(curmacro, r)
|
|
|
|
}
|
2020-02-14 23:52:20 +03:00
|
|
|
h.Relocate()
|
2019-08-03 00:48:59 +03:00
|
|
|
h.PluginCBRune("onRune", r)
|
2018-08-29 06:30:39 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-22 00:58:30 +03:00
|
|
|
// VSplitIndex opens the given buffer in a vertical split on the given side.
|
2020-02-06 01:16:31 +03:00
|
|
|
func (h *BufPane) VSplitIndex(buf *buffer.Buffer, right bool) *BufPane {
|
|
|
|
e := NewBufPaneFromBuf(buf, h.tab)
|
|
|
|
e.splitID = MainTab().GetNode(h.splitID).VSplit(right)
|
2019-01-10 04:07:18 +03:00
|
|
|
MainTab().Panes = append(MainTab().Panes, e)
|
|
|
|
MainTab().Resize()
|
|
|
|
MainTab().SetActive(len(MainTab().Panes) - 1)
|
2019-08-06 06:43:34 +03:00
|
|
|
return e
|
2019-01-05 01:40:56 +03:00
|
|
|
}
|
2021-08-22 00:58:30 +03:00
|
|
|
|
|
|
|
// HSplitIndex opens the given buffer in a horizontal split on the given side.
|
2020-02-06 01:16:31 +03:00
|
|
|
func (h *BufPane) HSplitIndex(buf *buffer.Buffer, bottom bool) *BufPane {
|
|
|
|
e := NewBufPaneFromBuf(buf, h.tab)
|
|
|
|
e.splitID = MainTab().GetNode(h.splitID).HSplit(bottom)
|
2019-01-10 04:07:18 +03:00
|
|
|
MainTab().Panes = append(MainTab().Panes, e)
|
|
|
|
MainTab().Resize()
|
|
|
|
MainTab().SetActive(len(MainTab().Panes) - 1)
|
2019-08-06 06:43:34 +03:00
|
|
|
return e
|
2019-01-05 01:40:56 +03:00
|
|
|
}
|
2020-02-06 01:16:31 +03:00
|
|
|
|
2021-08-22 00:58:30 +03:00
|
|
|
// VSplitBuf opens the given buffer in a new vertical split.
|
2020-02-06 01:16:31 +03:00
|
|
|
func (h *BufPane) VSplitBuf(buf *buffer.Buffer) *BufPane {
|
|
|
|
return h.VSplitIndex(buf, h.Buf.Settings["splitright"].(bool))
|
|
|
|
}
|
2021-08-22 00:58:30 +03:00
|
|
|
|
|
|
|
// HSplitBuf opens the given buffer in a new horizontal split.
|
2020-02-06 01:16:31 +03:00
|
|
|
func (h *BufPane) HSplitBuf(buf *buffer.Buffer) *BufPane {
|
|
|
|
return h.HSplitIndex(buf, h.Buf.Settings["splitbottom"].(bool))
|
|
|
|
}
|
2021-08-22 00:58:30 +03:00
|
|
|
|
|
|
|
// Close this pane.
|
2019-01-19 23:37:59 +03:00
|
|
|
func (h *BufPane) Close() {
|
2019-01-14 05:06:58 +03:00
|
|
|
h.Buf.Close()
|
|
|
|
}
|
2019-01-05 01:40:56 +03:00
|
|
|
|
2021-08-22 00:58:30 +03:00
|
|
|
// SetActive marks this pane as active.
|
2019-08-04 09:53:33 +03:00
|
|
|
func (h *BufPane) SetActive(b bool) {
|
|
|
|
h.BWindow.SetActive(b)
|
|
|
|
if b {
|
|
|
|
// Display any gutter messages for this line
|
|
|
|
c := h.Buf.GetActiveCursor()
|
|
|
|
none := true
|
|
|
|
for _, m := range h.Buf.Messages {
|
|
|
|
if c.Y == m.Start.Y || c.Y == m.End.Y {
|
|
|
|
InfoBar.GutterMessage(m.Msg)
|
|
|
|
none = false
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if none && InfoBar.HasGutter {
|
|
|
|
InfoBar.ClearGutter()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-01-03 23:27:43 +03:00
|
|
|
// BufKeyActions contains the list of all possible key actions the bufhandler could execute
|
2018-08-28 02:53:08 +03:00
|
|
|
var BufKeyActions = map[string]BufKeyAction{
|
2020-04-30 07:54:02 +03:00
|
|
|
"CursorUp": (*BufPane).CursorUp,
|
|
|
|
"CursorDown": (*BufPane).CursorDown,
|
|
|
|
"CursorPageUp": (*BufPane).CursorPageUp,
|
|
|
|
"CursorPageDown": (*BufPane).CursorPageDown,
|
|
|
|
"CursorLeft": (*BufPane).CursorLeft,
|
|
|
|
"CursorRight": (*BufPane).CursorRight,
|
|
|
|
"CursorStart": (*BufPane).CursorStart,
|
|
|
|
"CursorEnd": (*BufPane).CursorEnd,
|
|
|
|
"SelectToStart": (*BufPane).SelectToStart,
|
|
|
|
"SelectToEnd": (*BufPane).SelectToEnd,
|
|
|
|
"SelectUp": (*BufPane).SelectUp,
|
|
|
|
"SelectDown": (*BufPane).SelectDown,
|
|
|
|
"SelectLeft": (*BufPane).SelectLeft,
|
|
|
|
"SelectRight": (*BufPane).SelectRight,
|
|
|
|
"WordRight": (*BufPane).WordRight,
|
|
|
|
"WordLeft": (*BufPane).WordLeft,
|
|
|
|
"SelectWordRight": (*BufPane).SelectWordRight,
|
|
|
|
"SelectWordLeft": (*BufPane).SelectWordLeft,
|
|
|
|
"DeleteWordRight": (*BufPane).DeleteWordRight,
|
|
|
|
"DeleteWordLeft": (*BufPane).DeleteWordLeft,
|
|
|
|
"SelectLine": (*BufPane).SelectLine,
|
|
|
|
"SelectToStartOfLine": (*BufPane).SelectToStartOfLine,
|
|
|
|
"SelectToStartOfText": (*BufPane).SelectToStartOfText,
|
|
|
|
"SelectToStartOfTextToggle": (*BufPane).SelectToStartOfTextToggle,
|
|
|
|
"SelectToEndOfLine": (*BufPane).SelectToEndOfLine,
|
|
|
|
"ParagraphPrevious": (*BufPane).ParagraphPrevious,
|
|
|
|
"ParagraphNext": (*BufPane).ParagraphNext,
|
|
|
|
"InsertNewline": (*BufPane).InsertNewline,
|
|
|
|
"Backspace": (*BufPane).Backspace,
|
|
|
|
"Delete": (*BufPane).Delete,
|
|
|
|
"InsertTab": (*BufPane).InsertTab,
|
|
|
|
"Save": (*BufPane).Save,
|
|
|
|
"SaveAll": (*BufPane).SaveAll,
|
|
|
|
"SaveAs": (*BufPane).SaveAs,
|
|
|
|
"Find": (*BufPane).Find,
|
2020-05-17 19:22:33 +03:00
|
|
|
"FindLiteral": (*BufPane).FindLiteral,
|
2020-04-30 07:54:02 +03:00
|
|
|
"FindNext": (*BufPane).FindNext,
|
|
|
|
"FindPrevious": (*BufPane).FindPrevious,
|
2023-04-21 01:23:35 +03:00
|
|
|
"DiffNext": (*BufPane).DiffNext,
|
|
|
|
"DiffPrevious": (*BufPane).DiffPrevious,
|
2020-04-30 07:54:02 +03:00
|
|
|
"Center": (*BufPane).Center,
|
|
|
|
"Undo": (*BufPane).Undo,
|
|
|
|
"Redo": (*BufPane).Redo,
|
|
|
|
"Copy": (*BufPane).Copy,
|
|
|
|
"CopyLine": (*BufPane).CopyLine,
|
|
|
|
"Cut": (*BufPane).Cut,
|
|
|
|
"CutLine": (*BufPane).CutLine,
|
|
|
|
"DuplicateLine": (*BufPane).DuplicateLine,
|
|
|
|
"DeleteLine": (*BufPane).DeleteLine,
|
|
|
|
"MoveLinesUp": (*BufPane).MoveLinesUp,
|
|
|
|
"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,
|
|
|
|
"OpenFile": (*BufPane).OpenFile,
|
|
|
|
"Start": (*BufPane).Start,
|
|
|
|
"End": (*BufPane).End,
|
|
|
|
"PageUp": (*BufPane).PageUp,
|
|
|
|
"PageDown": (*BufPane).PageDown,
|
|
|
|
"SelectPageUp": (*BufPane).SelectPageUp,
|
|
|
|
"SelectPageDown": (*BufPane).SelectPageDown,
|
|
|
|
"HalfPageUp": (*BufPane).HalfPageUp,
|
|
|
|
"HalfPageDown": (*BufPane).HalfPageDown,
|
|
|
|
"StartOfText": (*BufPane).StartOfText,
|
|
|
|
"StartOfTextToggle": (*BufPane).StartOfTextToggle,
|
|
|
|
"StartOfLine": (*BufPane).StartOfLine,
|
|
|
|
"EndOfLine": (*BufPane).EndOfLine,
|
|
|
|
"ToggleHelp": (*BufPane).ToggleHelp,
|
|
|
|
"ToggleKeyMenu": (*BufPane).ToggleKeyMenu,
|
|
|
|
"ToggleDiffGutter": (*BufPane).ToggleDiffGutter,
|
|
|
|
"ToggleRuler": (*BufPane).ToggleRuler,
|
2021-09-28 23:39:03 +03:00
|
|
|
"ToggleHighlightSearch": (*BufPane).ToggleHighlightSearch,
|
|
|
|
"UnhighlightSearch": (*BufPane).UnhighlightSearch,
|
2020-04-30 07:54:02 +03:00
|
|
|
"ClearStatus": (*BufPane).ClearStatus,
|
|
|
|
"ShellMode": (*BufPane).ShellMode,
|
|
|
|
"CommandMode": (*BufPane).CommandMode,
|
|
|
|
"ToggleOverwriteMode": (*BufPane).ToggleOverwriteMode,
|
|
|
|
"Escape": (*BufPane).Escape,
|
|
|
|
"Quit": (*BufPane).Quit,
|
|
|
|
"QuitAll": (*BufPane).QuitAll,
|
2021-03-02 05:55:49 +03:00
|
|
|
"ForceQuit": (*BufPane).ForceQuit,
|
2020-04-30 07:54:02 +03:00
|
|
|
"AddTab": (*BufPane).AddTab,
|
|
|
|
"PreviousTab": (*BufPane).PreviousTab,
|
|
|
|
"NextTab": (*BufPane).NextTab,
|
|
|
|
"NextSplit": (*BufPane).NextSplit,
|
|
|
|
"PreviousSplit": (*BufPane).PreviousSplit,
|
|
|
|
"Unsplit": (*BufPane).Unsplit,
|
|
|
|
"VSplit": (*BufPane).VSplitAction,
|
|
|
|
"HSplit": (*BufPane).HSplitAction,
|
|
|
|
"ToggleMacro": (*BufPane).ToggleMacro,
|
|
|
|
"PlayMacro": (*BufPane).PlayMacro,
|
|
|
|
"Suspend": (*BufPane).Suspend,
|
|
|
|
"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,
|
|
|
|
"JumpLine": (*BufPane).JumpLine,
|
2020-07-04 04:02:16 +03:00
|
|
|
"Deselect": (*BufPane).Deselect,
|
|
|
|
"ClearInfo": (*BufPane).ClearInfo,
|
2020-04-30 07:54:02 +03:00
|
|
|
"None": (*BufPane).None,
|
2018-08-28 02:53:08 +03:00
|
|
|
|
|
|
|
// This was changed to InsertNewline but I don't want to break backwards compatibility
|
2020-04-30 07:54:02 +03:00
|
|
|
"InsertEnter": (*BufPane).InsertNewline,
|
2018-08-28 02:53:08 +03:00
|
|
|
}
|
2019-01-03 23:27:43 +03:00
|
|
|
|
|
|
|
// BufMouseActions contains the list of all possible mouse actions the bufhandler could execute
|
2018-08-28 02:53:08 +03:00
|
|
|
var BufMouseActions = map[string]BufMouseAction{
|
2019-01-19 23:37:59 +03:00
|
|
|
"MousePress": (*BufPane).MousePress,
|
2022-10-26 00:31:50 +03:00
|
|
|
"MouseDrag": (*BufPane).MouseDrag,
|
|
|
|
"MouseRelease": (*BufPane).MouseRelease,
|
2019-01-19 23:37:59 +03:00
|
|
|
"MouseMultiCursor": (*BufPane).MouseMultiCursor,
|
2018-08-28 02:53:08 +03:00
|
|
|
}
|
2019-01-03 07:26:40 +03:00
|
|
|
|
|
|
|
// MultiActions is a list of actions that should be executed multiple
|
|
|
|
// 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
|
2019-12-25 20:54:51 +03:00
|
|
|
var MultiActions = map[string]bool{
|
2020-04-11 00:21:02 +03:00
|
|
|
"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,
|
|
|
|
"SelectToStartOfTextToggle": true,
|
|
|
|
"SelectToEndOfLine": true,
|
|
|
|
"ParagraphPrevious": true,
|
|
|
|
"ParagraphNext": true,
|
|
|
|
"InsertNewline": true,
|
|
|
|
"Backspace": true,
|
|
|
|
"Delete": true,
|
|
|
|
"InsertTab": true,
|
|
|
|
"FindNext": true,
|
|
|
|
"FindPrevious": true,
|
2020-04-30 07:06:54 +03:00
|
|
|
"CopyLine": true,
|
2020-07-05 04:26:36 +03:00
|
|
|
"Copy": true,
|
2020-04-11 00:21:02 +03:00
|
|
|
"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,
|
|
|
|
"StartOfTextToggle": true,
|
|
|
|
"EndOfLine": true,
|
|
|
|
"JumpToMatchingBrace": true,
|
2019-01-03 07:26:40 +03:00
|
|
|
}
|