ref: 7e2b193e327849b76e0f5ae1a1185d09f9e38886
parent: 3303feea766c1cb8ef992c13765aaa9d2e179338
author: Ori Bernstein <ori@eigenstate.org>
date: Fri Aug 25 19:49:53 EDT 2017
More work on rendering.
--- a/irc.myr
+++ b/irc.myr
@@ -12,6 +12,7 @@
type client = struct
srv : server#[:]
+ logdir : byte[:]
self : chan#
focus : std.size
term : termdraw.term#
@@ -36,14 +37,22 @@
type chan = struct
log : std.fd
- buf : byte[:]
+ hist : (std.time, hist)[:]
+ histoff : std.size /* scrolling */
name : byte[:]
topic : byte[:]
users : byte[:][:]
stale : bool
- gutter : std.size
+ gutter : int
;;
+type hist = union
+ `Msg (byte[:], byte[:])
+ `Join byte[:]
+ `Part byte[:]
+ `Status byte[:]
+;;
+
const main = {var cli : client#
var c
@@ -67,7 +76,7 @@
}
const redial = {cli- for s in cli.srv
+ for s : cli.srv
if s.live
continue
;;
@@ -79,7 +88,7 @@
continue
;;
s.live = true
- for c in s.chan
+ for c : s.chan
send(cli, s, "JOIN {}\r\n", c.name);;
| `std.Err e:
@@ -129,7 +138,7 @@
| "PING": send(cli, srv, "PONG :{}\r\n", args[0])| "JOIN": joined(cli, srv, src, args)
| c:
- status(cli, "unknown server command {}\n", ln)+ status(cli, "unknown server command {}", ln);;
std.slfree(args)
std.slfree(ln)
@@ -141,7 +150,7 @@
if args.len == 1
c = name2chan(srv, args[0])
- status(cli, "joined {}: {}\n", args[0], src)+ status(cli, "joined {}: {}", args[0], src)match std.strfind(src, "!")
| `std.Some i: name = src[:i]
| `std.None: name = src
@@ -177,7 +186,7 @@
-> void
;;
c = name2chan(srv, args[2])
- for n in std.bysplit(args[3], " ")
+ for n : std.bysplit(args[3], " ")
n = std.strstrip(n)
match std.lsearch(c.users, n, std.strcmp)
| `std.None: std.slpush(&c.users, std.sldup(n))
@@ -188,18 +197,18 @@
}
const fd2srv = {cli, fd- for s in cli.srv
+ for s : cli.srv
if s.fd == fd
-> s
;;
;;
- std.fatal("missing srv for fd {}\n", fd)+ std.fatal("missing srv for fd {}", fd)}
const name2chan = {srv, chanvar new
- for c in srv.chan
+ for c : srv.chan
if std.sleq(c.name, chan)
-> c
;;
@@ -229,7 +238,7 @@
const closed = {cli, srv, msgif srv.live
srv.live = false
- status(cli, "{} closed: {}\n", srv.ds, msg)+ status(cli, "{} closed: {}", srv.ds, msg);;
}
@@ -239,6 +248,7 @@
t = termdraw.mk(std.In)
termdraw.raw(t)
+ home = fileutil.homedir()
user = std.getenvv("user", std.getenvv("USER", "user"))nick = user
cli = std.mk([
@@ -248,13 +258,13 @@
.self = mkchan("status", "status"),.focus = -1,
.term = t,
+ .logdir = std.pathcat(home, ".irclogs")
])
- home = fileutil.homedir()
path = std.pathcat(home, ".ircrc")
match bio.open(path, bio.Rd)
| `std.Ok cfg:
- for ln in bio.byline(cfg)
+ for ln : bio.byline(cfg)
do(cli, ln)
;;
| `std.Err e:
@@ -261,6 +271,8 @@
;;
std.slfree(path)
+ std.mkpath(cli.logdir)
+
-> cli
}
@@ -278,7 +290,7 @@
const mkchan = {name, topic-> std.mk([
.log = -1, /* FIXME: log to file */
- .buf = "", /* FIXME: read back from log */
+ .hist = [][:], /* FIXME: read back from log */
.name = std.sldup(name),
.topic = std.sldup(topic),
])
@@ -320,7 +332,7 @@
| "help": help(cli, sp[1:])
| "nick": changenick(cli, sp[1:])
| "user": changeuser(cli, sp[1:])
- | c: status(cli, "unknown command: /{}\n", text)+ | c: status(cli, "unknown command: /{}", text);;
std.slfree(sp)
}
@@ -327,7 +339,7 @@
const changenick = {cli, argsif args.len != 1
- status(cli, "/nick: invalid args {j= }\n", args)+ status(cli, "/nick: invalid args {j= }", args)-> void
;;
@@ -344,7 +356,7 @@
const changeuser = {cli, argsif args.len != 1
- status(cli, "/nick: invalid args {j= }\n", args)+ status(cli, "/nick: invalid args {j= }", args)-> void
;;
@@ -365,21 +377,21 @@
ds = ""
match args.len
| 1: ds = std.netaddr(args[0], "tcp", "ircd")
- | 0: status(cli, "join: missing server\n")
- | _: status(cli, "join: invalid arguments '{j= }'\n", args)+ | 0: status(cli, "join: missing server")
+ | _: status(cli, "join: invalid arguments '{j= }'", args);;
if ds.len == 0
-> void
;;
- for s in cli.srv
+ for s : cli.srv
if std.sleq(s.ds, ds)
- status(cli, "already connected to {j= }\n", args)+ status(cli, "already connected to {j= }", args)-> void
;;
;;
- status(cli, "dialing {}\n", ds)+ status(cli, "dialing {}", ds)match std.dial(ds)
| `std.Ok fd:
srv = mksrv(cli, fd, ds)
@@ -389,14 +401,14 @@
cli.focus = cli.srv.len - 1
srv.live = true
;;
- status(cli, "connected to {}\n", ds)+ status(cli, "connected to {}", ds)else
- status(cli, "could not handshake with {j= }\n", args)+ status(cli, "could not handshake with {j= }", args)std.close(srv.fd)
std.free(srv)
;;
| `std.Err e:
- status(cli, "failed to connect: {}\n", e)+ status(cli, "failed to connect: {}", e);;
}
@@ -406,7 +418,7 @@
if args.len == 0
c = curchan(cli)
elif args.len == 1
- -> status(cli, "names: invalid arguments: {j= }\n", args)+ -> status(cli, "names: invalid arguments: {j= }", args);;
c = curchan(cli)
if c != cli.self
@@ -426,15 +438,15 @@
if args.len == 2
match findsrv(cli, args[1])
| `std.Some s: send(cli, s, "JOIN {}\r\n", args[0])- | `std.None: status(cli, "no server '{}\n", args[1])+ | `std.None: status(cli, "no server '{}", args[1]);;
elif args.len == 1
match cursrv(cli)
| `std.Some s: send(cli, s, "JOIN {}\r\n", args[0])- | `std.None: status(cli, "no server focused\n")
+ | `std.None: status(cli, "no server focused")
;;
else
- -> status(cli, "invalid arguments: {j= }\n", args)+ -> status(cli, "invalid arguments: {j= }", args);;
}
@@ -450,18 +462,18 @@
elif args.len == 1
name = args[0]
else
- status(cli, "leave: invalid arguments: {j= }\n", args)+ status(cli, "leave: invalid arguments: {j= }", args)-> void
;;
- for srv in cli.srv
+ for srv : cli.srv
idx = 0
- for chan in srv.chan
+ for chan : srv.chan
idx++
if !std.sleq(chan.name, name)
continue
;;
- send(cli, srv, "PART {}\n", name)+ send(cli, srv, "PART {}\r\n", name)std.sldel(&srv.chan, idx)
freechan(chan)
;;
@@ -473,7 +485,7 @@
var ch, srv
if args.len != 1
- status(cli, "invalid arguments: {j= }\n", args)+ status(cli, "invalid arguments: {j= }", args)else
srvidx = -1
idx = -1
@@ -489,7 +501,7 @@
| `std.None: /* ok */
| `std.Some n:
if idx != -1
- status(cli, "ambiguous channel name {}\n", args[0])+ status(cli, "ambiguous channel name {}", args[0])-> void
else
srvidx = i
@@ -512,7 +524,7 @@
const quit = {cli, argsif args.len != 0
- status(cli, "invalid argument for quit: {j= }\n", args)+ status(cli, "invalid argument for quit: {j= }", args)-> void
;;
termdraw.free(cli.term)
@@ -520,14 +532,14 @@
}
const help = {cli, args- status(cli, "irc.myr commands\n")
- status(cli, "\t/connect dialstr: connect to server\n")
- status(cli, "\t/help [cmd...]: get help [on cmd...]\n")
- status(cli, "\t/join chan [srv]: join channel on server\n")
- status(cli, "\t/leave [chan]: leave current channel\n")
- status(cli, "\t/chan name: switch to channnel 'name'\n")
- status(cli, "\t/win name: switch to channnel 'name'\n")
- status(cli, "\t/quit: exit irc.myr\n")
+ status(cli, "irc.myr commands")
+ status(cli, "\t/connect dialstr: connect to server")
+ status(cli, "\t/help [cmd...]: get help [on cmd...]")
+ status(cli, "\t/join chan [srv]: join channel on server")
+ status(cli, "\t/leave [chan]: leave current channel")
+ status(cli, "\t/chan name: switch to channnel 'name'")
+ status(cli, "\t/win name: switch to channnel 'name'")
+ status(cli, "\t/quit: exit irc.myr")
}
const send = {cli, srv, fmt, args@@ -548,13 +560,13 @@
| `std.Some s:
c = curchan(cli)
if c == cli.self
- status(cli, "can't send to status channel\n")
+ status(cli, "can't send to status channel")
else
send(cli, s, "PRIVMSG {} :{}\r\n", c.name, msg)chanmsg(cli, c, s.nick, msg)
;;
| `std.None:
- status(cli, "no connected server\n")
+ status(cli, "no connected server")
;;
}
@@ -621,9 +633,8 @@
| `termdraw.Kdown:
| `termdraw.Winsz _:
| r:
- s = std.fmt("{}\n", r)- std.sljoin(&cli.self.buf, s)
- std.slfree(s)
+ s = std.fmt("{}", r)+ std.slpush(&cli.self.hist, (std.now(), `Status s))
;;
}
@@ -663,23 +674,84 @@
}
const drawtext = {cli, x0, y0, x1, y1, c- var t
- var b, x, y
+ var height, margin, width, off
+ var count, len
+ var t, dx, dy
+ var x, y
- x = x0
- y = y0
t = cli.term
- b = visiblerange(cli, c, y1 - y0)
-
termdraw.setbg(t, termdraw.Default)
termdraw.clear(t, x0, y0, x1, y1)
- for l in std.bychar(b)
+ termdraw.move(t, x0, y0)
+
+ count = 0
+ height = 0
+ off = 0
+ dx = x1 - x0
+ dy = y1 - y0
+ for (tm, h) : std.byreverse(c.hist)
+ margin = 0
+ match h
+ | `Msg (m, ln):
+ margin = c.gutter
+ width = termdraw.strwidth(t, ln)
+ height += width / (dx - margin)
+ | `Join user:
+ width = termdraw.strwidth(t, "#joined: ")
+ width += termdraw.strwidth(t, user)
+ height += width / dx
+ | `Part user:
+ width = termdraw.strwidth(t, "#parted: ")
+ width += termdraw.strwidth(t, user)
+ height += width / dx
+ | `Status msg:
+ width = termdraw.strwidth(t, "! ")
+ width += termdraw.strwidth(t, msg)
+ height += width / dx
+ ;;
+ count++
+ if height == dy
+ break
+ elif height > dy
+ off = dx * (dy - 1) + len % dx
+ break
+ ;;
+ ;;
+
+ x = x0
+ y = y0
+ for (tm, h) : c.hist[c.hist.len - count:]
+ match h
+ | `Msg (m, ln):
+ (x, y) = draw(t, m, x0, y, x1, y1)
+ (x, y) = draw(t, ln[off:], x0 + c.gutter, y, x1, y1)
+ | `Join user:
+ (x, y) = draw(t, "#joined ", x0, y, x1, y1)
+ (x, y) = draw(t, user, x, y, x1, y1)
+ | `Part user:
+ (x, y) = draw(t, "#parted ", x0, y, x1, y1)
+ (x, y) = draw(t, user, x, y0, y, y1)
+ | `Status msg:
+ (x, y) = draw(t, "! : ", x0, y, x1, y1)
+ (x, y) = draw(t, msg[off:], x, y, x1, y1)
+ ;;
+ y++
+ off = 0
+ ;;
+}
+
+const draw = {t, msg, x0, y0, x1, y1+ var x, y
+
+ x = x0
+ y = y0
+ for l : std.bychar(msg)
match l
| '\t':
x = (x / 8 + 1)*8
| '\n':
- x = 0
- y++
+ termdraw.put(t, "\\n")
+ x += 2
| chr:
if x < x1
termdraw.move(t, x, y)
@@ -688,7 +760,7 @@
;;
;;
if x >= x1
- x = (c.gutter : int)
+ x = x0
y++
termdraw.move(t, x, y)
@@ -697,6 +769,7 @@
x += 2
;;
;;
+ -> (x, y)
}
const drawlist = {cli, x0, y0, x1, y1, c@@ -708,8 +781,8 @@
termdraw.move(t, x0, y0)
termdraw.putc(t, '|')
termdraw.put(t, "[{}]", cli.self.name)- for s in cli.srv
- for ch in s.chan
+ for s : cli.srv
+ for ch : s.chan
if ch.stale
termdraw.put(t, "[*{}]", ch.name)else
@@ -730,27 +803,9 @@
termdraw.put(t, "[{}] {}", c.name, cli.input)}
-const visiblerange = {cli, c, dy- var n, i
-
- n = 0
- if c.buf.len == 0
- -> ""
- ;;
- for i = c.buf.len - 1; i > 0; i--
- if n == dy
- break
- ;;
- if c.buf[i] == ('\n' : byte)- n++
- ;;
- ;;
- -> c.buf[i:]
-}
-
const srvmsg = {cli, argsif args.len >= 2
- status(cli, "{}\n", args[1])+ status(cli, "{}", args[1]);;
}
@@ -760,8 +815,7 @@
c = cli.self
ap = std.vastart(&args)
s = std.fmtv(fmt, &ap)
- std.sljoin(&c.buf, s)
- std.slfree(s)
+ std.slpush(&c.hist, (std.now(), `Status s))
if c != curchan(cli)
c.stale = true
@@ -798,7 +852,7 @@
match std.strfind(cli.srv[i].ds, name)
| `std.Some o:
if idx != -1
- status(cli, "ambiguous server name {}\n", name)+ status(cli, "ambiguous server name {}", name)-> `std.None
else
idx = i
@@ -824,7 +878,7 @@
.events=sys.Pollin,
.revents=0,
])
- for s in cli.srv
+ for s : cli.srv
std.slpush(&pfd, [
.fd=(s.fd : sys.fd),
.events=sys.Pollin,
@@ -854,14 +908,7 @@
| `std.Some i: nick = sender[:i]
| `std.None: nick = sender
;;
- std.sljoin(&chan.buf, nick)
- for var i = 0; i < chan.gutter - nick.len; i++
- std.sljoin(&chan.buf, " ")
- ;;
- std.sljoin(&chan.buf, "| ")
- std.sljoin(&chan.buf, msg)
- std.sljoin(&chan.buf, "\n")
-
+ std.slpush(&chan.hist, (std.now(), `Msg (nick, msg)))
if chan != curchan(cli)
chan.stale = true
;;
@@ -869,12 +916,6 @@
}
const chanstatus = {cli, chan, msg- for var i = 0; i < chan.gutter; i++
- std.sljoin(&chan.buf, " ")
- ;;
- std.sljoin(&chan.buf, "~~= ")
- std.sljoin(&chan.buf, msg)
- std.sljoin(&chan.buf, " =~~\n")
}
const writeall = {fd, buf--
⑨