/reply command with autocomplete.
authorAndrey Petrov <andrey.petrov@shazow.net>
Mon, 19 Jan 2015 02:11:30 +0000 (18:11 -0800)
committerAndrey Petrov <andrey.petrov@shazow.net>
Mon, 19 Jan 2015 02:11:30 +0000 (18:11 -0800)
chat/command.go
chat/user.go
host.go

index aa6d3f5ab7c5e9483fd49df58c1b8df1cc9c35e1..047ea527a05e03873c1f769b5f460417bfc15227 100644 (file)
@@ -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()
index df8114b865dd3ec3c136441c7ede1c968f864d23..32be8caa2ddfdc795a8bd093f2814427d9b7d5b0 100644 (file)
@@ -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 c88e46c1b57eea872c438deb8d7f10d40eca737e..8d87142cbfa1764ae95a23d295c2998a48f14534 100644 (file)
--- 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
-       }
+// 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
+               }
 
-       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 == "" {
+               if strings.HasSuffix(line[:pos], " ") {
+                       // Don't autocomplete spaces.
                        return
                }
-               if isFirst {
-                       completed += ":"
+
+               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
        }
-       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,