sshchat: Echo command messages with the new timestamp code.
authorAndrey Petrov <andrey.petrov@shazow.net>
Sun, 17 Mar 2019 17:50:01 +0000 (13:50 -0400)
committerAndrey Petrov <andrey.petrov@shazow.net>
Sun, 17 Mar 2019 17:50:01 +0000 (13:50 -0400)
chat/message/user.go
host.go
host_test.go

index 7635161d74279da0d6b9615bdd5c091d3d6ca6de..6a9e17c8bb85631fea2b07ea9e0f59a15b2f52d5 100644 (file)
@@ -170,6 +170,8 @@ func (u *User) render(m Message) string {
                if cfg.Bell {
                        out += Bel
                }
+       case *CommandMsg:
+               out += m.RenderSelf(cfg)
        default:
                out += m.Render(cfg.Theme)
        }
diff --git a/host.go b/host.go
index 9db14797df8caa1fea8dfc306cb977fc8419d606..176187bf34b2c3f1a1ef55d1110fd3c78f5cc876 100644 (file)
--- a/host.go
+++ b/host.go
@@ -162,6 +162,20 @@ func (h *Host) Connect(term *sshd.Terminal) {
 
                m := message.ParseInput(line, user)
 
+               // Gross hack to override line echo in golang.org/x/crypto/ssh/terminal
+               // It needs to live before we render the resulting message.
+               term.Write([]byte{
+                       27, '[', 'A', // Up
+                       27, '[', '2', 'K', // Clear line
+               })
+               // May the gods have mercy on our souls.
+
+               if m, ok := m.(*message.CommandMsg); ok {
+                       // Other messages render themselves by the room, commands we'll
+                       // have to re-echo ourselves manually.
+                       user.HandleMsg(m)
+               }
+
                // FIXME: Any reason to use h.room.Send(m) instead?
                h.HandleMsg(m)
 
@@ -175,15 +189,6 @@ func (h *Host) Connect(term *sshd.Terminal) {
                        term.SetPrompt(GetPrompt(user))
                        user.SetHighlight(user.Name())
                }
-
-               // Gross hack to override line echo in golang.org/x/crypto/ssh/terminal
-               term.Write([]byte{
-                       27,       // keyEscape
-                       '[', 'A', // Up
-                       27,            // keyEscape
-                       '[', '2', 'K', // Clear line
-               })
-               // May the gods have mercy on our souls.
        }
 
        err = h.Leave(user)
index b4ee362b12a23f7a980a55d2d9311d8e18cc9856..cfb88f41121322897c57d077fb68267880c00967 100644 (file)
@@ -6,6 +6,7 @@ import (
        "crypto/rsa"
        "errors"
        "io"
+       mathRand "math/rand"
        "strings"
        "testing"
 
@@ -15,22 +16,53 @@ import (
 )
 
 func stripPrompt(s string) string {
-       pos := strings.LastIndex(s, "\033[K")
-       if pos < 0 {
-               return s
+       // FIXME: Is there a better way to do this?
+       if endPos := strings.Index(s, "\x1b[K "); endPos > 0 {
+               return s[endPos+3:]
+       }
+       if endPos := strings.Index(s, "\x1b[2K "); endPos > 0 {
+               return s[endPos+4:]
+       }
+       if endPos := strings.Index(s, "] "); endPos > 0 {
+               return s[endPos+2:]
+       }
+       return s
+}
+
+func TestStripPrompt(t *testing.T) {
+       tests := []struct {
+               Input string
+               Want  string
+       }{
+               {
+                       Input: "\x1b[A\x1b[2K[quux] hello",
+                       Want:  "hello",
+               },
+               {
+                       Input: "[foo] \x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[D\x1b[K * Guest1 joined. (Connected: 2)\r",
+                       Want:  " * Guest1 joined. (Connected: 2)\r",
+               },
+       }
+
+       for i, tc := range tests {
+               if got, want := stripPrompt(tc.Input), tc.Want; got != want {
+                       t.Errorf("case #%d:\n got: %q\nwant: %q", i, got, want)
+               }
        }
-       return s[pos+3:]
 }
 
 func TestHostGetPrompt(t *testing.T) {
        var expected, actual string
 
+       // Make the random colors consistent across tests
+       mathRand.Seed(1)
+
        u := message.NewUser(&Identity{id: "foo"})
 
        actual = GetPrompt(u)
        expected = "[foo] "
        if actual != expected {
-               t.Errorf("Got: %q; Expected: %q", actual, expected)
+               t.Errorf("Invalid host prompt:\n Got: %q;\nWant: %q", actual, expected)
        }
 
        u.SetConfig(message.UserConfig{
@@ -39,7 +71,7 @@ func TestHostGetPrompt(t *testing.T) {
        actual = GetPrompt(u)
        expected = "[\033[38;05;88mfoo\033[0m] "
        if actual != expected {
-               t.Errorf("Got: %q; Expected: %q", actual, expected)
+               t.Errorf("Invalid host prompt:\n Got: %q;\nWant: %q", actual, expected)
        }
 }
 
@@ -205,18 +237,23 @@ func TestHostKick(t *testing.T) {
                        // Change nicks, make sure op sticks
                        w.Write([]byte("/nick quux\r\n"))
                        scanner.Scan() // Prompt
+                       scanner.Scan() // Prompt echo
                        scanner.Scan() // Nick change response
 
+                       // Signal for the second client to connect
+                       connected <- struct{}{}
+
                        // Block until second client is here
                        connected <- struct{}{}
                        scanner.Scan() // Connected message
 
                        w.Write([]byte("/kick bar\r\n"))
                        scanner.Scan() // Prompt
+                       scanner.Scan() // Prompt echo
 
-                       scanner.Scan()
+                       scanner.Scan() // Kick result
                        if actual, expected := stripPrompt(scanner.Text()), " * bar was kicked by quux.\r"; actual != expected {
-                               t.Errorf("Got %q; expected %q", actual, expected)
+                               t.Errorf("Failed to detect kick:\n Got: %q;\nWant: %q", actual, expected)
                        }
 
                        kicked <- struct{}{}
@@ -231,6 +268,8 @@ func TestHostKick(t *testing.T) {
        }()
 
        go func() {
+               <-connected
+
                // Second client
                err := sshd.ConnectShell(addr, "bar", func(r io.Reader, w io.WriteCloser) error {
                        scanner := bufio.NewScanner(r)