From 544c9789c039172f95faca20fde83184c7f515fc Mon Sep 17 00:00:00 2001 From: Andrey Petrov Date: Sun, 18 Jan 2015 18:11:30 -0800 Subject: [PATCH] /reply command with autocomplete. --- chat/command.go | 2 +- chat/user.go | 15 ++++++- host.go | 103 +++++++++++++++++++++++++++++++----------------- 3 files changed, 81 insertions(+), 39 deletions(-) diff --git a/chat/command.go b/chat/command.go index aa6d3f5..047ea52 100644 --- a/chat/command.go +++ b/chat/command.go @@ -201,7 +201,7 @@ func InitCommands(c *Commands) { c.Add(Command{ Prefix: "/quiet", - Help: "Silence announcement-type messages (join, part, rename, etc).", + Help: "Silence room announcements.", Handler: func(room *Room, msg CommandMsg) error { u := msg.From() u.ToggleQuietMode() diff --git a/chat/user.go b/chat/user.go index df8114b..32be8ca 100644 --- a/chat/user.go +++ b/chat/user.go @@ -64,6 +64,16 @@ func (u *User) SetId(id Id) { u.SetColorIdx(rand.Int()) } +// ReplyTo returns the last user that messaged this user. +func (u *User) ReplyTo() *User { + return u.replyTo +} + +// SetReplyTo sets the last user to message this user. +func (u *User) SetReplyTo(user *User) { + u.replyTo = user +} + // ToggleQuietMode will toggle whether or not quiet mode is enabled func (u *User) ToggleQuietMode() { u.Config.Quiet = !u.Config.Quiet @@ -113,10 +123,13 @@ func (u *User) SetHighlight(s string) error { return nil } -func (u User) render(m Message) string { +func (u *User) render(m Message) string { switch m := m.(type) { case *PublicMsg: return m.RenderFor(u.Config) + Newline + case *PrivateMsg: + u.SetReplyTo(m.From()) + return m.Render(u.Config.Theme) + Newline default: return m.Render(u.Config.Theme) + Newline } diff --git a/host.go b/host.go index c88e46c..8d87142 100644 --- a/host.go +++ b/host.go @@ -68,8 +68,6 @@ func (h Host) isOp(conn sshd.Connection) bool { // Connect a specific Terminal to this host and its room. func (h *Host) Connect(term *sshd.Terminal) { id := NewIdentity(term.Conn) - term.AutoCompleteCallback = h.AutoCompleteFunction - user := chat.NewUserScreen(id, term) user.Config.Theme = h.theme go func() { @@ -92,6 +90,7 @@ func (h *Host) Connect(term *sshd.Terminal) { // Successfully joined. term.SetPrompt(GetPrompt(user)) + term.AutoCompleteCallback = h.AutoCompleteFunction(user) user.SetHighlight(user.Name()) h.count++ @@ -159,44 +158,52 @@ func (h Host) completeCommand(partial string) string { return "" } -// AutoCompleteFunction is a callback for terminal autocompletion -func (h *Host) AutoCompleteFunction(line string, pos int, key rune) (newLine string, newPos int, ok bool) { - if key != 9 { - return - } - - if strings.HasSuffix(line[:pos], " ") { - // Don't autocomplete spaces. - return - } - - fields := strings.Fields(line[:pos]) - isFirst := len(fields) < 2 - partial := fields[len(fields)-1] - posPartial := pos - len(partial) - - var completed string - if isFirst && strings.HasPrefix(partial, "/") { - // Command - completed = h.completeCommand(partial) - } else { - // Name - completed = h.completeName(partial) - if completed == "" { +// AutoCompleteFunction returns a callback for terminal autocompletion +func (h *Host) AutoCompleteFunction(u *chat.User) func(line string, pos int, key rune) (newLine string, newPos int, ok bool) { + return func(line string, pos int, key rune) (newLine string, newPos int, ok bool) { + if key != 9 { return } - if isFirst { - completed += ":" - } - } - completed += " " - // Reposition the cursor - newLine = strings.Replace(line[posPartial:], partial, completed, 1) - newLine = line[:posPartial] + newLine - newPos = pos + (len(completed) - len(partial)) - ok = true - return + if strings.HasSuffix(line[:pos], " ") { + // Don't autocomplete spaces. + return + } + + fields := strings.Fields(line[:pos]) + isFirst := len(fields) < 2 + partial := fields[len(fields)-1] + posPartial := pos - len(partial) + + var completed string + if isFirst && strings.HasPrefix(partial, "/") { + // Command + completed = h.completeCommand(partial) + if completed == "/reply" { + replyTo := u.ReplyTo() + if replyTo != nil { + completed = "/msg " + replyTo.Name() + } + } + } else { + // Name + completed = h.completeName(partial) + if completed == "" { + return + } + if isFirst { + completed += ":" + } + } + completed += " " + + // Reposition the cursor + newLine = strings.Replace(line[posPartial:], partial, completed, 1) + newLine = line[:posPartial] + newLine + newPos = pos + (len(completed) - len(partial)) + ok = true + return + } } // GetUser returns a chat.User based on a name. @@ -235,6 +242,28 @@ func (h *Host) InitCommands(c *chat.Commands) { }, }) + c.Add(chat.Command{ + Prefix: "/reply", + PrefixHelp: "MESSAGE", + Help: "Reply with MESSAGE to the previous private message.", + Handler: func(room *chat.Room, msg chat.CommandMsg) error { + args := msg.Args() + switch len(args) { + case 0: + return errors.New("must specify message") + } + + target := msg.From().ReplyTo() + if target == nil { + return errors.New("no message to reply to") + } + + m := chat.NewPrivateMsg(strings.Join(args, " "), msg.From(), target) + room.Send(m) + return nil + }, + }) + // Op commands c.Add(chat.Command{ Op: true,