Add search functionality

Fixes #21
This commit is contained in:
erroneousboat 2017-07-15 23:56:39 +02:00
parent b6030cbdaa
commit d2bce7f66a
5 changed files with 102 additions and 0 deletions

View File

@ -59,6 +59,16 @@ Getting started
"C-8": "backspace",
"<delete>": "delete",
"<space>": "space"
},
"search": {
"<left>": "cursor-left",
"<right>": "cursor-right",
"<escape>": "clear-input",
"<enter>": "clear-input",
"<backspace>": "backspace",
"C-8": "backspace",
"<delete>": "delete",
"<space>": "space"
}
}
}
@ -79,6 +89,7 @@ Default Key Mapping
| mode | key | action |
|---------|-----------|----------------------------|
| command | `i` | insert mode |
| command | `/` | search mode |
| command | `k` | move channel cursor up |
| command | `j` | move channel cursor down |
| command | `g` | move channel cursor top |
@ -95,3 +106,5 @@ Default Key Mapping
| insert | `right` | move input cursor right |
| insert | `enter` | send message |
| insert | `esc` | command mode |
| search | `esc` | command mode |
| search | `enter` | command mode |

View File

@ -195,6 +195,7 @@ func (c *Channels) MoveCursorBottom() {
// ScrollUp enables us to scroll through the channel list when it overflows
func (c *Channels) ScrollUp() {
// Is cursor at the top of the channel view?
if c.CursorPosition == c.List.InnerBounds().Min.Y {
if c.Offset > 0 {
c.Offset--
@ -206,6 +207,7 @@ func (c *Channels) ScrollUp() {
// ScrollDown enables us to scroll through the channel list when it overflows
func (c *Channels) ScrollDown() {
// Is the cursor at the bottom of the channel view?
if c.CursorPosition == c.List.InnerBounds().Max.Y-1 {
if c.Offset < len(c.List.Items)-1 {
c.Offset++
@ -215,6 +217,45 @@ func (c *Channels) ScrollDown() {
}
}
// Search will search through the channels to find a channel,
// when a match has been found the selected channel will then
// be the channel that has been found
func (c *Channels) Search(term string) {
for i, item := range c.List.Items {
if strings.Contains(item, term) {
// The new position
newPos := i
// Is the new position in range of the current view?
minRange := c.Offset
maxRange := c.Offset + c.List.InnerBounds().Max.Y - 1
if newPos < minRange {
// newPos is above, we need to scroll up.
c.SetSelectedChannel(i)
// How much do we need to scroll to get it into range?
c.Offset = c.Offset - (minRange - newPos)
} else if newPos > maxRange {
// newPos is below, we need to scroll down
c.SetSelectedChannel(i)
// How much do we need to scroll to get it into range?
c.Offset = c.Offset + (newPos - maxRange) + 1
} else {
// newPos is inside range
c.SetSelectedChannel(i)
}
// Set cursor to correct position
c.CursorPosition = (newPos - minRange) + 1
break
}
}
}
// SetNotification will be called when a new message arrives and will
// render an notification icon in front of the channel name
func (c *Channels) SetNotification(svc *service.SlackService, channelID string) {

View File

@ -28,6 +28,7 @@ func NewConfig(filepath string) (*Config, error) {
KeyMap: map[string]keyMapping{
"command": {
"i": "mode-insert",
"/": "mode-search",
"k": "channel-up",
"j": "channel-down",
"g": "channel-top",
@ -51,6 +52,16 @@ func NewConfig(filepath string) (*Config, error) {
"<delete>": "delete",
"<space>": "space",
},
"search": {
"<left>": "cursor-left",
"<right>": "cursor-right",
"<escape>": "clear-input",
"<enter>": "clear-input",
"<backspace>": "backspace",
"C-8": "backspace",
"<delete>": "delete",
"<space>": "space",
},
},
}

View File

@ -14,6 +14,7 @@ import (
const (
CommandMode = "command"
InsertMode = "insert"
SearchMode = "search"
)
type AppContext struct {

View File

@ -28,6 +28,8 @@ var actionMap = map[string]func(*context.AppContext){
"quit": actionQuit,
"mode-insert": actionInsertMode,
"mode-command": actionCommandMode,
"mode-search": actionSearchMode,
"clear-input": actionClearInput,
"channel-up": actionMoveCursorUpChannels,
"channel-down": actionMoveCursorDownChannels,
"channel-top": actionMoveCursorTopChannels,
@ -67,6 +69,8 @@ func anyKeyHandler(ctx *context.AppContext) {
} else {
if ctx.Mode == context.InsertMode && ev.Ch != 0 {
actionInput(ctx.View, ev.Ch)
} else if ctx.Mode == context.SearchMode && ev.Ch != 0 {
actionSearch(ctx, ev.Ch)
}
}
}
@ -133,6 +137,15 @@ func actionInput(view *views.View, key rune) {
termui.Render(view.Input)
}
func actionClearInput(ctx *context.AppContext) {
// Clear input
ctx.View.Input.Clear()
ctx.View.Refresh()
// Set command mode
actionCommandMode(ctx)
}
func actionSpace(ctx *context.AppContext) {
actionInput(ctx.View, ' ')
}
@ -174,6 +187,23 @@ func actionSend(ctx *context.AppContext) {
}
}
func actionSearch(ctx *context.AppContext, key rune) {
go func() {
if timer != nil {
timer.Stop()
}
actionInput(ctx.View, key)
timer = time.NewTimer(time.Second / 4)
<-timer.C
term := ctx.View.Input.GetText()
ctx.View.Channels.Search(term)
actionChangeChannel(ctx)
}()
}
// actionQuit will exit the program by using os.Exit, this is
// done because we are using a custom termui EvtStream. Which
// we won't be able to call termui.StopLoop() on. See main.go
@ -194,6 +224,12 @@ func actionCommandMode(ctx *context.AppContext) {
termui.Render(ctx.View.Mode)
}
func actionSearchMode(ctx *context.AppContext) {
ctx.Mode = context.SearchMode
ctx.View.Mode.Par.Text = "SEARCH"
termui.Render(ctx.View.Mode)
}
func actionGetMessages(ctx *context.AppContext) {
ctx.View.Chat.GetMessages(
ctx.Service,