summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app.go186
-rw-r--r--irc/events.go6
-rw-r--r--irc/session.go88
-rw-r--r--ui/buffers.go53
4 files changed, 229 insertions, 104 deletions
diff --git a/app.go b/app.go
index 7b416c8..00c31d2 100644
--- a/app.go
+++ b/app.go
@@ -642,21 +642,9 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) {
Highlight: true,
})
case irc.UserNickEvent:
- var body ui.StyledStringBuilder
- body.WriteString(fmt.Sprintf("%s\u2192%s", ev.FormerNick, ev.User))
- textStyle := tcell.StyleDefault.Foreground(tcell.ColorGray)
- arrowStyle := tcell.StyleDefault
- body.AddStyle(0, textStyle)
- body.AddStyle(len(ev.FormerNick), arrowStyle)
- body.AddStyle(body.Len()-len(ev.User), textStyle)
+ line := app.formatEvent(ev)
for _, c := range s.ChannelsSharedWith(ev.User) {
- app.win.AddLine(netID, c, ui.NotifyNone, ui.Line{
- At: msg.TimeOrNow(),
- Head: "--",
- HeadColor: tcell.ColorGray,
- Body: body.StyledString(),
- Mergeable: true,
- })
+ app.win.AddLine(netID, c, ui.NotifyNone, line)
}
case irc.SelfJoinEvent:
i, added := app.win.AddBuffer(netID, "", ev.Channel)
@@ -685,70 +673,27 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) {
app.lastBuffer = ""
}
case irc.UserJoinEvent:
- var body ui.StyledStringBuilder
- body.Grow(len(ev.User) + 1)
- body.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorGreen))
- body.WriteByte('+')
- body.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorGray))
- body.WriteString(ev.User)
- app.win.AddLine(netID, ev.Channel, ui.NotifyNone, ui.Line{
- At: msg.TimeOrNow(),
- Head: "--",
- HeadColor: tcell.ColorGray,
- Body: body.StyledString(),
- Mergeable: true,
- })
+ line := app.formatEvent(ev)
+ app.win.AddLine(netID, ev.Channel, ui.NotifyNone, line)
case irc.SelfPartEvent:
app.win.RemoveBuffer(netID, ev.Channel)
delete(app.messageBounds, boundKey{netID, ev.Channel})
case irc.UserPartEvent:
- var body ui.StyledStringBuilder
- body.Grow(len(ev.User) + 1)
- body.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorRed))
- body.WriteByte('-')
- body.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorGray))
- body.WriteString(ev.User)
- app.win.AddLine(netID, ev.Channel, ui.NotifyNone, ui.Line{
- At: msg.TimeOrNow(),
- Head: "--",
- HeadColor: tcell.ColorGray,
- Body: body.StyledString(),
- Mergeable: true,
- })
+ line := app.formatEvent(ev)
+ app.win.AddLine(netID, ev.Channel, ui.NotifyNone, line)
case irc.UserQuitEvent:
- var body ui.StyledStringBuilder
- body.Grow(len(ev.User) + 1)
- body.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorRed))
- body.WriteByte('-')
- body.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorGray))
- body.WriteString(ev.User)
+ line := app.formatEvent(ev)
for _, c := range ev.Channels {
- app.win.AddLine(netID, c, ui.NotifyNone, ui.Line{
- At: msg.TimeOrNow(),
- Head: "--",
- HeadColor: tcell.ColorGray,
- Body: body.StyledString(),
- Mergeable: true,
- })
+ app.win.AddLine(netID, c, ui.NotifyNone, line)
}
case irc.TopicChangeEvent:
+ line := app.formatEvent(ev)
+ app.win.AddLine(netID, ev.Channel, ui.NotifyUnread, line)
topic := ui.IRCString(ev.Topic).String()
- body := fmt.Sprintf("Topic changed to: %s", topic)
app.win.SetTopic(netID, ev.Channel, topic)
- app.win.AddLine(netID, ev.Channel, ui.NotifyUnread, ui.Line{
- At: msg.TimeOrNow(),
- Head: "--",
- HeadColor: tcell.ColorGray,
- Body: ui.Styled(body, tcell.StyleDefault.Foreground(tcell.ColorGray)),
- })
case irc.ModeChangeEvent:
- body := fmt.Sprintf("Mode change: %s", ev.Mode)
- app.win.AddLine(netID, ev.Channel, ui.NotifyUnread, ui.Line{
- At: msg.TimeOrNow(),
- Head: "--",
- HeadColor: tcell.ColorGray,
- Body: ui.Styled(body, tcell.StyleDefault.Foreground(tcell.ColorGray)),
- })
+ line := app.formatEvent(ev)
+ app.win.AddLine(netID, ev.Channel, ui.NotifyUnread, line)
case irc.InviteEvent:
var buffer string
var notify ui.NotifyType
@@ -807,19 +752,25 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) {
var linesAfter []ui.Line
bounds, hasBounds := app.messageBounds[boundKey{netID, ev.Target}]
for _, m := range ev.Messages {
+ var line ui.Line
switch ev := m.(type) {
case irc.MessageEvent:
- _, line, _ := app.formatMessage(s, ev)
- if hasBounds {
- c := bounds.Compare(&line)
- if c < 0 {
- linesBefore = append(linesBefore, line)
- } else if c > 0 {
- linesAfter = append(linesAfter, line)
- }
- } else {
+ _, line, _ = app.formatMessage(s, ev)
+ default:
+ line = app.formatEvent(ev)
+ }
+ if line.IsZero() {
+ continue
+ }
+ if hasBounds {
+ c := bounds.Compare(&line)
+ if c < 0 {
linesBefore = append(linesBefore, line)
+ } else if c > 0 {
+ linesAfter = append(linesAfter, line)
}
+ } else {
+ linesBefore = append(linesBefore, line)
}
}
app.win.AddLines(netID, ev.Target, linesBefore, linesAfter)
@@ -987,6 +938,89 @@ func (app *App) completions(cursorIdx int, text []rune) []ui.Completion {
return cs
}
+// formatEvent returns a formatted ui.Line for an irc.Event.
+func (app *App) formatEvent(ev irc.Event) ui.Line {
+ switch ev := ev.(type) {
+ case irc.UserNickEvent:
+ var body ui.StyledStringBuilder
+ body.WriteString(fmt.Sprintf("%s\u2192%s", ev.FormerNick, ev.User))
+ textStyle := tcell.StyleDefault.Foreground(tcell.ColorGray)
+ arrowStyle := tcell.StyleDefault
+ body.AddStyle(0, textStyle)
+ body.AddStyle(len(ev.FormerNick), arrowStyle)
+ body.AddStyle(body.Len()-len(ev.User), textStyle)
+
+ return ui.Line{
+ At: ev.Time,
+ Head: "--",
+ HeadColor: tcell.ColorGray,
+ Body: body.StyledString(),
+ Mergeable: true,
+ }
+ case irc.UserJoinEvent:
+ var body ui.StyledStringBuilder
+ body.Grow(len(ev.User) + 1)
+ body.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorGreen))
+ body.WriteByte('+')
+ body.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorGray))
+ body.WriteString(ev.User)
+ return ui.Line{
+ At: ev.Time,
+ Head: "--",
+ HeadColor: tcell.ColorGray,
+ Body: body.StyledString(),
+ Mergeable: true,
+ }
+ case irc.UserPartEvent:
+ var body ui.StyledStringBuilder
+ body.Grow(len(ev.User) + 1)
+ body.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorRed))
+ body.WriteByte('-')
+ body.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorGray))
+ body.WriteString(ev.User)
+ return ui.Line{
+ At: ev.Time,
+ Head: "--",
+ HeadColor: tcell.ColorGray,
+ Body: body.StyledString(),
+ Mergeable: true,
+ }
+ case irc.UserQuitEvent:
+ var body ui.StyledStringBuilder
+ body.Grow(len(ev.User) + 1)
+ body.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorRed))
+ body.WriteByte('-')
+ body.SetStyle(tcell.StyleDefault.Foreground(tcell.ColorGray))
+ body.WriteString(ev.User)
+ return ui.Line{
+ At: ev.Time,
+ Head: "--",
+ HeadColor: tcell.ColorGray,
+ Body: body.StyledString(),
+ Mergeable: true,
+ }
+ case irc.TopicChangeEvent:
+ topic := ui.IRCString(ev.Topic).String()
+ body := fmt.Sprintf("Topic changed to: %s", topic)
+ return ui.Line{
+ At: ev.Time,
+ Head: "--",
+ HeadColor: tcell.ColorGray,
+ Body: ui.Styled(body, tcell.StyleDefault.Foreground(tcell.ColorGray)),
+ }
+ case irc.ModeChangeEvent:
+ body := fmt.Sprintf("Mode change: %s", ev.Mode)
+ return ui.Line{
+ At: ev.Time,
+ Head: "--",
+ HeadColor: tcell.ColorGray,
+ Body: ui.Styled(body, tcell.StyleDefault.Foreground(tcell.ColorGray)),
+ }
+ default:
+ return ui.Line{}
+ }
+}
+
// formatMessage sets how a given message must be formatted.
//
// It computes three things:
diff --git a/irc/events.go b/irc/events.go
index 70a6c1d..0c84f2b 100644
--- a/irc/events.go
+++ b/irc/events.go
@@ -19,6 +19,7 @@ type SelfNickEvent struct {
type UserNickEvent struct {
User string
FormerNick string
+ Time time.Time
}
type SelfJoinEvent struct {
@@ -30,6 +31,7 @@ type SelfJoinEvent struct {
type UserJoinEvent struct {
User string
Channel string
+ Time time.Time
}
type SelfPartEvent struct {
@@ -39,21 +41,25 @@ type SelfPartEvent struct {
type UserPartEvent struct {
User string
Channel string
+ Time time.Time
}
type UserQuitEvent struct {
User string
Channels []string
+ Time time.Time
}
type TopicChangeEvent struct {
Channel string
Topic string
+ Time time.Time
}
type ModeChangeEvent struct {
Channel string
Mode string
+ Time time.Time
}
type InviteEvent struct {
diff --git a/irc/session.go b/irc/session.go
index 8467f2c..bbf01f6 100644
--- a/irc/session.go
+++ b/irc/session.go
@@ -58,6 +58,7 @@ var SupportedCapabilities = map[string]struct{}{
"setname": {},
"draft/chathistory": {},
+ "draft/event-playback": {},
"soju.im/bouncer-networks": {},
}
@@ -527,18 +528,23 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
}
s.targetsBatch.Targets[target] = t
} else if b, ok := s.chBatches[id]; ok {
- ev, err := s.newMessageEvent(msg)
+ ev, err := s.handleMessageRegistered(msg, true)
if err != nil {
return nil, err
}
- s.chBatches[id] = HistoryEvent{
- Target: b.Target,
- Messages: append(b.Messages, ev),
+ if ev != nil {
+ s.chBatches[id] = HistoryEvent{
+ Target: b.Target,
+ Messages: append(b.Messages, ev),
+ }
+ return nil, nil
}
- return nil, nil
}
}
+ return s.handleMessageRegistered(msg, false)
+}
+func (s *Session) handleMessageRegistered(msg Message, playback bool) (Event, error) {
switch msg.Command {
case "AUTHENTICATE":
if s.auth == nil {
@@ -663,6 +669,14 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
return nil, err
}
+ if playback {
+ return UserJoinEvent{
+ User: msg.Prefix.Name,
+ Channel: channel,
+ Time: msg.TimeOrNow(),
+ }, nil
+ }
+
nickCf := s.Casemap(msg.Prefix.Name)
channelCf := s.Casemap(channel)
@@ -685,6 +699,7 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
return UserJoinEvent{
User: msg.Prefix.Name,
Channel: c.Name,
+ Time: msg.TimeOrNow(),
}, nil
}
case "PART":
@@ -697,6 +712,14 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
return nil, err
}
+ if playback {
+ return UserPartEvent{
+ User: msg.Prefix.Name,
+ Channel: channel,
+ Time: msg.TimeOrNow(),
+ }, nil
+ }
+
nickCf := s.Casemap(msg.Prefix.Name)
channelCf := s.Casemap(channel)
@@ -718,6 +741,7 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
return UserPartEvent{
User: u.Name.Name,
Channel: c.Name,
+ Time: msg.TimeOrNow(),
}, nil
}
}
@@ -727,6 +751,14 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
return nil, err
}
+ if playback {
+ return UserPartEvent{
+ User: nick,
+ Channel: channel,
+ Time: msg.TimeOrNow(),
+ }, nil
+ }
+
nickCf := s.Casemap(nick)
channelCf := s.Casemap(channel)
@@ -748,6 +780,7 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
return UserPartEvent{
User: nick,
Channel: c.Name,
+ Time: msg.TimeOrNow(),
}, nil
}
}
@@ -756,6 +789,13 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
return nil, errMissingPrefix
}
+ if playback {
+ return UserQuitEvent{
+ User: msg.Prefix.Name,
+ Time: msg.TimeOrNow(),
+ }, nil
+ }
+
nickCf := s.Casemap(msg.Prefix.Name)
if u, ok := s.users[nickCf]; ok {
@@ -771,6 +811,7 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
return UserQuitEvent{
User: u.Name.Name,
Channels: channels,
+ Time: msg.TimeOrNow(),
}, nil
}
case rplNamreply:
@@ -864,6 +905,14 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
return nil, err
}
+ if playback {
+ return TopicChangeEvent{
+ Channel: channel,
+ Topic: topic,
+ Time: msg.TimeOrNow(),
+ }, nil
+ }
+
channelCf := s.Casemap(channel)
if c, ok := s.channels[channelCf]; ok {
@@ -874,6 +923,7 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
return TopicChangeEvent{
Channel: c.Name,
Topic: c.Topic,
+ Time: msg.TimeOrNow(),
}, nil
}
case "MODE":
@@ -882,6 +932,14 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
return nil, err
}
+ if playback {
+ return ModeChangeEvent{
+ Channel: channel,
+ Mode: mode,
+ Time: msg.TimeOrNow(),
+ }, nil
+ }
+
channelCf := s.Casemap(channel)
if c, ok := s.channels[channelCf]; ok {
@@ -917,7 +975,8 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
s.channels[channelCf] = c
return ModeChangeEvent{
Channel: c.Name,
- Mode: strings.Join(msg.Params[1:], " "),
+ Mode: mode,
+ Time: msg.TimeOrNow(),
}, nil
}
case "INVITE":
@@ -966,12 +1025,20 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
return nil, err
}
+ if playback {
+ return s.newMessageEvent(msg)
+ }
+
targetCf := s.casemap(target)
nickCf := s.casemap(msg.Prefix.Name)
s.typings.Done(targetCf, nickCf)
return s.newMessageEvent(msg)
case "TAGMSG":
+ if playback {
+ return nil, nil
+ }
+
if msg.Prefix == nil {
return nil, errMissingPrefix
}
@@ -1048,6 +1115,14 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
return nil, err
}
+ if playback {
+ return UserNickEvent{
+ User: nick,
+ FormerNick: msg.Prefix.Name,
+ Time: msg.TimeOrNow(),
+ }, nil
+ }
+
nickCf := s.Casemap(msg.Prefix.Name)
newNick := nick
newNickCf := s.Casemap(newNick)
@@ -1070,6 +1145,7 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
return UserNickEvent{
User: nick,
FormerNick: msg.Prefix.Name,
+ Time: msg.TimeOrNow(),
}, nil
}
case "BOUNCER":
diff --git a/ui/buffers.go b/ui/buffers.go
index 9a20abe..d277491 100644
--- a/ui/buffers.go
+++ b/ui/buffers.go
@@ -39,6 +39,21 @@ type Line struct {
newLines []int
}
+func (l *Line) IsZero() bool {
+ return l.Body.string == ""
+}
+
+func (l *Line) Merge(line Line) {
+ newBody := new(StyledStringBuilder)
+ newBody.Grow(len(l.Body.string) + 2 + len(line.Body.string))
+ newBody.WriteStyledString(l.Body)
+ newBody.WriteString(" ")
+ newBody.WriteStyledString(line.Body)
+ l.Body = newBody.StyledString()
+ l.computeSplitPoints()
+ l.width = 0
+}
+
func (l *Line) computeSplitPoints() {
if l.splitPoints == nil {
l.splitPoints = []point{}
@@ -307,14 +322,7 @@ func (bs *BufferList) AddLine(netID, title string, notify NotifyType, line Line)
if line.Mergeable && n != 0 && b.lines[n-1].Mergeable {
l := &b.lines[n-1]
- newBody := new(StyledStringBuilder)
- newBody.Grow(len(l.Body.string) + 2 + len(line.Body.string))
- newBody.WriteStyledString(l.Body)
- newBody.WriteString(" ")
- newBody.WriteStyledString(line.Body)
- l.Body = newBody.StyledString()
- l.computeSplitPoints()
- l.width = 0
+ l.Merge(line)
// TODO change b.scrollAmt if it's not 0 and bs.current is idx.
} else {
line.computeSplitPoints()
@@ -340,21 +348,22 @@ func (bs *BufferList) AddLines(netID, title string, before, after []Line) {
b := &bs.list[idx]
- for i := 0; i < len(before); i++ {
- before[i].Body = before[i].Body.ParseURLs()
- before[i].computeSplitPoints()
- }
- for i := 0; i < len(after); i++ {
- after[i].Body = after[i].Body.ParseURLs()
- after[i].computeSplitPoints()
- }
-
- if len(before) != 0 {
- b.lines = append(before, b.lines...)
- }
- if len(after) != 0 {
- b.lines = append(b.lines, after...)
+ lines := make([]Line, 0, len(before)+len(b.lines)+len(after))
+ for _, buf := range []*[]Line{&before, &b.lines, &after} {
+ for _, line := range *buf {
+ if line.Mergeable && len(lines) > 0 && lines[len(lines)-1].Mergeable {
+ l := &lines[len(lines)-1]
+ l.Merge(line)
+ } else {
+ if buf != &b.lines {
+ line.Body = line.Body.ParseURLs()
+ line.computeSplitPoints()
+ }
+ lines = append(lines, line)
+ }
+ }
}
+ b.lines = lines
}
func (bs *BufferList) SetTopic(netID, title string, topic string) {