Merge branch 'mappable-keys' into v0.2.0

This commit is contained in:
erroneousboat 2016-10-29 17:59:34 +02:00
commit 02c086239e
4 changed files with 174 additions and 73 deletions

View File

@ -31,7 +31,35 @@ Getting started
"theme": "light",
// optional: set the width of the sidebar (between 1 and 11), default is 1
"sidebar_width": 3
"sidebar_width": 3,
// optional: define custom key mappings
// (shown are the default key mappings)
"key-map": {
"command": {
"i": "mode-insert",
"k": "channel-up",
"j": "channel-down",
"g": "channel-top",
"G": "channel-bottom",
"<previous>": "chat-up",
"C-b": "chat-up",
"C-u": "chat-up",
"<next>": "chat-down",
"C-f": "chat-down",
"C-d": "chat-down",
"q": "quit",
},
"insert": {
"<left>": "cursor-left",
"<right>": "cursor-right",
"<enter>": "send",
"<escape>": "mode-command",
"<backspace>": "backspace",
"<delete>": "delete",
"<space>": "space",
}
}
}
```
@ -44,15 +72,15 @@ Getting started
$ slack-term -config [path-to-config-file]
```
Usage
-----
Default Key Mapping
-------------------
| mode | key | action |
|--------|-----------|----------------------------|
| normal | `i` | insert mode |
| normal | `k` | move channel cursor up |
| normal | `j` | move channel cursor down |
| normal | `gg` | move channel cursor top |
| normal | `g` | move channel cursor top |
| normal | `G` | move channel cursor bottom |
| normal | `pg-up` | scroll chat pane up |
| normal | `ctrl-b` | scroll chat pane up |
@ -60,7 +88,6 @@ Usage
| normal | `pg-down` | scroll chat pane down |
| normal | `ctrl-f` | scroll chat pane down |
| normal | `ctrl-d` | scroll chat pane down |
| normal | `pg-down` | scroll chat pane down |
| normal | `q` | quit |
| insert | `left` | move input cursor left |
| insert | `right` | move input cursor right |

View File

@ -10,18 +10,46 @@ import (
// Config is the definition of a Config struct
type Config struct {
SlackToken string `json:"slack_token"`
Theme string `json:"theme"`
SidebarWidth int `json:"sidebar_width"`
MainWidth int `json:"-"`
SlackToken string `json:"slack_token"`
Theme string `json:"theme"`
SidebarWidth int `json:"sidebar_width"`
MainWidth int `json:"-"`
KeyMap map[string]keyMapping `json:"key-map"`
}
type keyMapping map[string]string
// NewConfig loads the config file and returns a Config struct
func NewConfig(filepath string) (*Config, error) {
cfg := Config{
Theme: "dark",
SidebarWidth: 1,
MainWidth: 11,
KeyMap: map[string]keyMapping{
"command": {
"i": "mode-insert",
"k": "channel-up",
"j": "channel-down",
"g": "channel-top",
"G": "channel-bottom",
"<previous>": "chat-up",
"C-b": "chat-up",
"C-u": "chat-up",
"<next>": "chat-down",
"C-f": "chat-down",
"C-d": "chat-down",
"q": "quit",
},
"insert": {
"<left>": "cursor-left",
"<right>": "cursor-right",
"<enter>": "send",
"<escape>": "mode-command",
"<backspace>": "backspace",
"<delete>": "delete",
"<space>": "space",
},
},
}
file, err := os.Open(filepath)

View File

@ -1,6 +1,8 @@
package handlers
import (
"strconv"
"github.com/gizak/termui"
"github.com/nlopes/slack"
termbox "github.com/nsf/termbox-go"
@ -9,6 +11,27 @@ import (
"github.com/erroneousboat/slack-term/views"
)
// actionMap binds specific action names to the function counterparts,
// these action names can then be used to bind them to specific keys
// in the Config.
var actionMap = map[string]func(*context.AppContext){
"space": actionSpace,
"backspace": actionBackSpace,
"delete": actionDelete,
"cursor-right": actionMoveCursorRight,
"cursor-left": actionMoveCursorLeft,
"send": actionSend,
"quit": actionQuit,
"mode-insert": actionInsertMode,
"mode-command": actionCommandMode,
"channel-up": actionMoveCursorUpChannels,
"channel-down": actionMoveCursorDownChannels,
"channel-top": actionMoveCursorTopChannels,
"channel-bottom": actionMoveCursorBottomChannels,
"chat-up": actionScrollUpChat,
"chat-down": actionScrollDownChat,
}
func RegisterEventHandlers(ctx *context.AppContext) {
anyKeyHandler(ctx)
incomingMessageHandler(ctx)
@ -20,56 +43,25 @@ func anyKeyHandler(ctx *context.AppContext) {
for {
ev := termbox.PollEvent()
if ev.Type == termbox.EventKey {
if ctx.Mode == context.CommandMode {
switch ev.Key {
case termbox.KeyPgup:
actionScrollUpChat(ctx)
case termbox.KeyCtrlB:
actionScrollUpChat(ctx)
case termbox.KeyCtrlU:
actionScrollUpChat(ctx)
case termbox.KeyPgdn:
actionScrollDownChat(ctx)
case termbox.KeyCtrlF:
actionScrollDownChat(ctx)
case termbox.KeyCtrlD:
actionScrollDownChat(ctx)
default:
switch ev.Ch {
case 'q':
actionQuit()
case 'j':
actionMoveCursorDownChannels(ctx)
case 'k':
actionMoveCursorUpChannels(ctx)
case 'g':
actionMoveCursorTopChannels(ctx)
case 'G':
actionMoveCursorBottomChannels(ctx)
case 'i':
actionInsertMode(ctx)
}
}
} else if ctx.Mode == context.InsertMode {
switch ev.Key {
case termbox.KeyEsc:
actionCommandMode(ctx)
case termbox.KeyEnter:
actionSend(ctx)
case termbox.KeySpace:
actionInput(ctx.View, ' ')
case termbox.KeyBackspace, termbox.KeyBackspace2:
actionBackSpace(ctx.View)
case termbox.KeyDelete:
actionDelete(ctx.View)
case termbox.KeyArrowRight:
actionMoveCursorRight(ctx.View)
case termbox.KeyArrowLeft:
actionMoveCursorLeft(ctx.View)
default:
actionInput(ctx.View, ev.Ch)
}
if ev.Type != termbox.EventKey {
continue
}
keyStr := getKeyString(ev)
// Get the action name (actionStr) from the key that
// has been pressed. If this is found try to uncover
// the associated function with this key and execute
// it.
actionStr, ok := ctx.Config.KeyMap[ctx.Mode][keyStr]
if ok {
action, ok := actionMap[actionStr]
if ok {
action(ctx)
}
} else {
if ctx.Mode == context.InsertMode && ev.Ch != 0 {
actionInput(ctx.View, ev.Ch)
}
}
}
@ -135,24 +127,28 @@ func actionInput(view *views.View, key rune) {
termui.Render(view.Input)
}
func actionBackSpace(view *views.View) {
view.Input.Backspace()
termui.Render(view.Input)
func actionSpace(ctx *context.AppContext) {
actionInput(ctx.View, ' ')
}
func actionDelete(view *views.View) {
view.Input.Delete()
termui.Render(view.Input)
func actionBackSpace(ctx *context.AppContext) {
ctx.View.Input.Backspace()
termui.Render(ctx.View.Input)
}
func actionMoveCursorRight(view *views.View) {
view.Input.MoveCursorRight()
termui.Render(view.Input)
func actionDelete(ctx *context.AppContext) {
ctx.View.Input.Delete()
termui.Render(ctx.View.Input)
}
func actionMoveCursorLeft(view *views.View) {
view.Input.MoveCursorLeft()
termui.Render(view.Input)
func actionMoveCursorRight(ctx *context.AppContext) {
ctx.View.Input.MoveCursorRight()
termui.Render(ctx.View.Input)
}
func actionMoveCursorLeft(ctx *context.AppContext) {
ctx.View.Input.MoveCursorLeft()
termui.Render(ctx.View.Input)
}
func actionSend(ctx *context.AppContext) {
@ -172,7 +168,7 @@ func actionSend(ctx *context.AppContext) {
}
}
func actionQuit() {
func actionQuit(*context.AppContext) {
termui.StopLoop()
}
@ -255,3 +251,53 @@ func actionScrollDownChat(ctx *context.AppContext) {
ctx.View.Chat.ScrollDown()
termui.Render(ctx.View.Chat)
}
// GetKeyString will return a string that resembles the key event from
// termbox. This is blatanly copied from termui because it is an unexported
// function.
//
// See:
// - https://github.com/gizak/termui/blob/a7e3aeef4cdf9fa2edb723b1541cb69b7bb089ea/events.go#L31-L72
// - https://github.com/nsf/termbox-go/blob/master/api_common.go
func getKeyString(e termbox.Event) string {
var ek string
k := string(e.Ch)
pre := ""
mod := ""
if e.Mod == termbox.ModAlt {
mod = "M-"
}
if e.Ch == 0 {
if e.Key > 0xFFFF-12 {
k = "<f" + strconv.Itoa(0xFFFF-int(e.Key)+1) + ">"
} else if e.Key > 0xFFFF-25 {
ks := []string{"<insert>", "<delete>", "<home>", "<end>", "<previous>", "<next>", "<up>", "<down>", "<left>", "<right>"}
k = ks[0xFFFF-int(e.Key)-12]
}
if e.Key <= 0x7F {
pre = "C-"
k = string('a' - 1 + int(e.Key))
kmap := map[termbox.Key][2]string{
termbox.KeyCtrlSpace: {"C-", "<space>"},
termbox.KeyBackspace: {"", "<backspace>"},
termbox.KeyTab: {"", "<tab>"},
termbox.KeyEnter: {"", "<enter>"},
termbox.KeyEsc: {"", "<escape>"},
termbox.KeyCtrlBackslash: {"C-", "\\"},
termbox.KeyCtrlSlash: {"C-", "/"},
termbox.KeySpace: {"", "<space>"},
termbox.KeyCtrl8: {"C-", "8"},
}
if sk, ok := kmap[e.Key]; ok {
pre = sk[0]
k = sk[1]
}
}
}
ek = pre + mod + k
return ek
}

View File

@ -32,7 +32,7 @@ var (
flgConfig string
flgUsage bool
VERSION = "v0.1.0"
VERSION = "v0.2.0"
)
func init() {