import (
"fmt"
+ "regexp"
"strings"
"time"
)
return fmt.Sprintf("%s: %s", t.ColorName(m.from), m.body)
}
+func (m *PublicMsg) RenderHighlighted(t *Theme, highlight *regexp.Regexp) string {
+ if highlight == nil || t == nil {
+ return m.Render(t)
+ }
+
+ body := highlight.ReplaceAllString(m.body, t.Highlight("${1}"))
+ return fmt.Sprintf("%s: %s", t.ColorName(m.from), body)
+}
+
func (m *PublicMsg) String() string {
return fmt.Sprintf("%s: %s", m.from.Name(), m.body)
}
*PublicMsg
command string
args []string
- room *Room
+ room *Room
}
func (m *CommandMsg) Command() string {
Newline = "\r\n"
)
-// Interface for Colors
-type Color interface {
+// Interface for Styles
+type Style interface {
String() string
Format(string) string
}
+// General hardcoded style, mostly used as a crutch until we flesh out the
+// framework to support backgrounds etc.
+type style string
+
+func (c style) String() string {
+ return string(c)
+}
+
+func (c style) Format(s string) string {
+ return c.String() + s + Reset
+}
+
// 256 color type, for terminals who support it
type Color256 uint8
// Container for a collection of colors
type Palette struct {
- colors []Color
+ colors []Style
size int
}
// Get a color by index, overflows are looped around.
-func (p Palette) Get(i int) Color {
+func (p Palette) Get(i int) Style {
return p.colors[i%(p.size-1)]
}
// Collection of settings for chat
type Theme struct {
- id string
- sys Color
- pm Color
- names *Palette
+ id string
+ sys Style
+ pm Style
+ highlight Style
+ names *Palette
}
func (t Theme) Id() string {
return t.sys.Format(s)
}
+// Highlight a matched string, usually name
+func (t Theme) Highlight(s string) string {
+ if t.highlight == nil {
+ return s
+ }
+ return t.highlight.Format(s)
+}
+
// List of initialzied themes
var Themes []Theme
func readableColors256() *Palette {
size := 247
p := Palette{
- colors: make([]Color, size),
+ colors: make([]Style, size),
size: size,
}
j := 0
Themes = []Theme{
Theme{
- id: "colors",
- names: palette,
- sys: palette.Get(8), // Grey
- pm: palette.Get(7), // White
+ id: "colors",
+ names: palette,
+ sys: palette.Get(8), // Grey
+ pm: palette.Get(7), // White
+ highlight: style(Bold + "\033[48;5;11m\033[38;5;16m"), // Yellow highlight
},
Theme{
id: "mono",
import (
"errors"
+ "fmt"
"io"
"math/rand"
+ "regexp"
"sync"
"time"
)
const messageBuffer = 20
+const reHighlight = `\b(%s)\b`
var ErrUserClosed = errors.New("user closed")
u.HandleMsg(<-u.msg, out)
}
+// SetHighlight sets the highlighting regular expression to match string.
+func (u *User) SetHighlight(s string) error {
+ re, err := regexp.Compile(fmt.Sprintf(reHighlight, s))
+ if err != nil {
+ return err
+ }
+ u.Config.Highlight = re
+ return nil
+}
+
+func (u User) render(m Message) string {
+ switch m := m.(type) {
+ case *PublicMsg:
+ return m.RenderHighlighted(u.Config.Theme, u.Config.Highlight) + Newline
+ default:
+ return m.Render(u.Config.Theme) + Newline
+ }
+}
+
func (u *User) HandleMsg(m Message, out io.Writer) {
- s := m.Render(u.Config.Theme)
- _, err := out.Write([]byte(s + Newline))
+ r := u.render(m)
+ _, err := out.Write([]byte(r))
if err != nil {
logger.Printf("Write failed to %s, closing: %s", u.Name(), err)
u.Close()
// Container for per-user configurations.
type UserConfig struct {
- Highlight bool
+ Highlight *regexp.Regexp
Bell bool
Quiet bool
Theme *Theme
func init() {
DefaultUserConfig = &UserConfig{
- Highlight: true,
- Bell: false,
- Quiet: false,
+ Bell: true,
+ Quiet: false,
}
// TODO: Seed random?
// Successfully joined.
term.SetPrompt(GetPrompt(user))
+ user.SetHighlight(user.Name())
h.count++
// Should the user be op'd on join?
// FIXME: This is hacky, how do we improve the API to allow for
// this? Chat module shouldn't know about terminals.
term.SetPrompt(GetPrompt(user))
+ user.SetHighlight(user.Name())
}
}