2019-01-11 05:26:58 +03:00
|
|
|
package action
|
|
|
|
|
|
|
|
import (
|
2020-02-11 03:09:03 +03:00
|
|
|
"errors"
|
2019-12-22 21:43:29 +03:00
|
|
|
"runtime"
|
2019-01-11 22:49:22 +03:00
|
|
|
|
2020-07-05 03:00:39 +03:00
|
|
|
"github.com/zyedidia/micro/v2/internal/clipboard"
|
2020-05-04 17:16:15 +03:00
|
|
|
"github.com/zyedidia/micro/v2/internal/display"
|
|
|
|
"github.com/zyedidia/micro/v2/internal/screen"
|
|
|
|
"github.com/zyedidia/micro/v2/internal/shell"
|
2020-09-05 21:52:35 +03:00
|
|
|
"github.com/zyedidia/tcell/v2"
|
2019-01-11 05:26:58 +03:00
|
|
|
"github.com/zyedidia/terminal"
|
|
|
|
)
|
|
|
|
|
2020-07-02 00:11:07 +03:00
|
|
|
type TermKeyAction func(*TermPane)
|
|
|
|
|
|
|
|
var TermBindings *KeyTree
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
TermBindings = NewKeyTree()
|
|
|
|
}
|
|
|
|
|
|
|
|
func TermKeyActionGeneral(a TermKeyAction) PaneKeyAction {
|
|
|
|
return func(p Pane) bool {
|
|
|
|
a(p.(*TermPane))
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TermMapEvent(k Event, action string) {
|
|
|
|
switch e := k.(type) {
|
|
|
|
case KeyEvent, KeySequenceEvent, RawEvent:
|
|
|
|
termMapKey(e, action)
|
|
|
|
case MouseEvent:
|
|
|
|
termMapMouse(e, action)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func termMapKey(k Event, action string) {
|
|
|
|
if f, ok := TermKeyActions[action]; ok {
|
|
|
|
TermBindings.RegisterKeyBinding(k, TermKeyActionGeneral(f))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func termMapMouse(k MouseEvent, action string) {
|
|
|
|
// TODO: map mouse
|
|
|
|
termMapKey(k, action)
|
|
|
|
}
|
|
|
|
|
2019-01-19 23:37:59 +03:00
|
|
|
type TermPane struct {
|
2019-01-11 05:26:58 +03:00
|
|
|
*shell.Terminal
|
|
|
|
display.Window
|
|
|
|
|
|
|
|
mouseReleased bool
|
2019-01-11 22:49:22 +03:00
|
|
|
id uint64
|
2020-02-06 01:16:31 +03:00
|
|
|
tab *Tab
|
2019-01-11 22:49:22 +03:00
|
|
|
}
|
|
|
|
|
2020-02-11 03:09:03 +03:00
|
|
|
func NewTermPane(x, y, w, h int, t *shell.Terminal, id uint64, tab *Tab) (*TermPane, error) {
|
|
|
|
if !TermEmuSupported {
|
|
|
|
return nil, errors.New("Terminal emulator is not supported on this system")
|
|
|
|
}
|
|
|
|
|
2019-01-19 23:37:59 +03:00
|
|
|
th := new(TermPane)
|
2019-01-11 22:49:22 +03:00
|
|
|
th.Terminal = t
|
|
|
|
th.id = id
|
2019-01-15 08:24:53 +03:00
|
|
|
th.mouseReleased = true
|
2019-01-11 22:49:22 +03:00
|
|
|
th.Window = display.NewTermWindow(x, y, w, h, t)
|
2020-02-06 01:16:31 +03:00
|
|
|
th.tab = tab
|
2020-02-11 03:09:03 +03:00
|
|
|
return th, nil
|
2019-01-11 22:49:22 +03:00
|
|
|
}
|
|
|
|
|
2019-01-19 23:37:59 +03:00
|
|
|
func (t *TermPane) ID() uint64 {
|
2019-01-11 22:49:22 +03:00
|
|
|
return t.id
|
|
|
|
}
|
|
|
|
|
2019-01-19 23:37:59 +03:00
|
|
|
func (t *TermPane) SetID(i uint64) {
|
|
|
|
t.id = i
|
|
|
|
}
|
|
|
|
|
2020-02-06 01:16:31 +03:00
|
|
|
func (t *TermPane) SetTab(tab *Tab) {
|
|
|
|
t.tab = tab
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *TermPane) Tab() *Tab {
|
|
|
|
return t.tab
|
|
|
|
}
|
|
|
|
|
2019-01-19 23:37:59 +03:00
|
|
|
func (t *TermPane) Close() {}
|
2019-01-14 05:06:58 +03:00
|
|
|
|
2020-07-02 00:11:07 +03:00
|
|
|
// Quit closes this termpane
|
2019-01-19 23:37:59 +03:00
|
|
|
func (t *TermPane) Quit() {
|
2019-01-14 05:06:58 +03:00
|
|
|
t.Close()
|
2019-01-11 22:49:22 +03:00
|
|
|
if len(MainTab().Panes) > 1 {
|
|
|
|
t.Unsplit()
|
|
|
|
} else if len(Tabs.List) > 1 {
|
|
|
|
Tabs.RemoveTab(t.id)
|
|
|
|
} else {
|
|
|
|
screen.Screen.Fini()
|
|
|
|
InfoBar.Close()
|
2019-12-22 21:43:29 +03:00
|
|
|
runtime.Goexit()
|
2019-01-11 22:49:22 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-02 00:11:07 +03:00
|
|
|
// Unsplit removes this split
|
2019-01-19 23:37:59 +03:00
|
|
|
func (t *TermPane) Unsplit() {
|
2019-01-11 22:49:22 +03:00
|
|
|
n := MainTab().GetNode(t.id)
|
|
|
|
n.Unsplit()
|
|
|
|
|
|
|
|
MainTab().RemovePane(MainTab().GetPane(t.id))
|
|
|
|
MainTab().Resize()
|
|
|
|
MainTab().SetActive(len(MainTab().Panes) - 1)
|
2019-01-11 05:26:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// HandleEvent handles a tcell event by forwarding it to the terminal emulator
|
|
|
|
// If the event is a mouse event and the program running in the emulator
|
|
|
|
// does not have mouse support, the emulator will support selections and
|
|
|
|
// copy-paste
|
2019-01-19 23:37:59 +03:00
|
|
|
func (t *TermPane) HandleEvent(event tcell.Event) {
|
2019-01-11 05:26:58 +03:00
|
|
|
if e, ok := event.(*tcell.EventKey); ok {
|
2020-07-02 00:11:07 +03:00
|
|
|
ke := KeyEvent{
|
|
|
|
code: e.Key(),
|
2020-09-06 04:59:19 +03:00
|
|
|
mod: metaToAlt(e.Modifiers()),
|
2020-07-02 00:11:07 +03:00
|
|
|
r: e.Rune(),
|
|
|
|
}
|
|
|
|
action, more := TermBindings.NextEvent(ke, nil)
|
|
|
|
|
|
|
|
if !more {
|
|
|
|
if action != nil {
|
|
|
|
action(t)
|
|
|
|
TermBindings.ResetEvents()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
TermBindings.ResetEvents()
|
|
|
|
}
|
|
|
|
|
|
|
|
if more {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-01-11 05:26:58 +03:00
|
|
|
if t.Status == shell.TTDone {
|
|
|
|
switch e.Key() {
|
|
|
|
case tcell.KeyEscape, tcell.KeyCtrlQ, tcell.KeyEnter:
|
|
|
|
t.Close()
|
2019-01-11 22:49:22 +03:00
|
|
|
t.Quit()
|
2019-01-11 05:26:58 +03:00
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if e.Key() == tcell.KeyCtrlC && t.HasSelection() {
|
2020-07-05 03:00:39 +03:00
|
|
|
clipboard.Write(t.GetSelection(t.GetView().Width), clipboard.ClipboardReg)
|
2019-01-11 05:26:58 +03:00
|
|
|
InfoBar.Message("Copied selection to clipboard")
|
|
|
|
} else if t.Status != shell.TTDone {
|
2020-01-01 05:50:26 +03:00
|
|
|
t.WriteString(event.EscSeq())
|
2019-01-11 05:26:58 +03:00
|
|
|
}
|
2020-02-08 03:17:17 +03:00
|
|
|
} else if _, ok := event.(*tcell.EventPaste); ok {
|
|
|
|
if t.Status != shell.TTDone {
|
|
|
|
t.WriteString(event.EscSeq())
|
|
|
|
}
|
2019-08-26 21:47:27 +03:00
|
|
|
} else if e, ok := event.(*tcell.EventMouse); e != nil && (!ok || t.State.Mode(terminal.ModeMouseMask)) {
|
2020-01-02 20:42:39 +03:00
|
|
|
// t.WriteString(event.EscSeq())
|
2019-08-26 21:47:27 +03:00
|
|
|
} else if e != nil {
|
2019-01-11 05:26:58 +03:00
|
|
|
x, y := e.Position()
|
|
|
|
v := t.GetView()
|
|
|
|
x -= v.X
|
2019-01-15 08:24:53 +03:00
|
|
|
y -= v.Y
|
2019-01-11 05:26:58 +03:00
|
|
|
|
|
|
|
if e.Buttons() == tcell.Button1 {
|
|
|
|
if !t.mouseReleased {
|
|
|
|
// drag
|
|
|
|
t.Selection[1].X = x
|
|
|
|
t.Selection[1].Y = y
|
|
|
|
} else {
|
|
|
|
t.Selection[0].X = x
|
|
|
|
t.Selection[0].Y = y
|
|
|
|
t.Selection[1].X = x
|
|
|
|
t.Selection[1].Y = y
|
|
|
|
}
|
|
|
|
|
|
|
|
t.mouseReleased = false
|
|
|
|
} else if e.Buttons() == tcell.ButtonNone {
|
|
|
|
if !t.mouseReleased {
|
|
|
|
t.Selection[1].X = x
|
|
|
|
t.Selection[1].Y = y
|
|
|
|
}
|
|
|
|
t.mouseReleased = true
|
|
|
|
}
|
|
|
|
}
|
2019-08-26 21:47:27 +03:00
|
|
|
|
|
|
|
if t.Status == shell.TTClose {
|
|
|
|
t.Quit()
|
|
|
|
}
|
2019-01-11 05:26:58 +03:00
|
|
|
}
|
2019-01-11 22:49:22 +03:00
|
|
|
|
2020-07-02 00:11:07 +03:00
|
|
|
// Exit closes the termpane
|
|
|
|
func (t *TermPane) Exit() {
|
|
|
|
t.Terminal.Close()
|
|
|
|
t.Quit()
|
|
|
|
}
|
|
|
|
|
|
|
|
// CommandMode opens the termpane's command mode
|
|
|
|
func (t *TermPane) CommandMode() {
|
|
|
|
InfoBar.Prompt("> ", "", "TerminalCommand", nil, func(resp string, canceled bool) {
|
|
|
|
if !canceled {
|
|
|
|
t.HandleCommand(resp)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// NextSplit moves to the next split
|
|
|
|
func (t *TermPane) NextSplit() {
|
|
|
|
a := t.tab.active
|
|
|
|
if a < len(t.tab.Panes)-1 {
|
|
|
|
a++
|
|
|
|
} else {
|
|
|
|
a = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
t.tab.SetActive(a)
|
|
|
|
}
|
|
|
|
|
|
|
|
// HandleCommand handles a command for the term pane
|
2019-01-19 23:37:59 +03:00
|
|
|
func (t *TermPane) HandleCommand(input string) {
|
2019-01-11 22:49:22 +03:00
|
|
|
InfoBar.Error("Commands are unsupported in term for now")
|
|
|
|
}
|
2020-07-02 00:11:07 +03:00
|
|
|
|
|
|
|
// TermKeyActions contains the list of all possible key actions the termpane could execute
|
|
|
|
var TermKeyActions = map[string]TermKeyAction{
|
|
|
|
"Exit": (*TermPane).Exit,
|
|
|
|
"CommandMode": (*TermPane).CommandMode,
|
|
|
|
"NextSplit": (*TermPane).NextSplit,
|
|
|
|
}
|