shithub: irc.myr

Download patch

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, chan
 	var 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, msg
 	if 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, args
 	if 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, args
 	if 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, args
 	if 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, args
 	if 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
--