commit dea8dc7c441cd67ceb42ef74ac8b82fc452445a2
parent ef5d8c7ed0ecf79ee95b51383a5b2cb42b601081
Author: Alexey Yerin <yyp@disroot.org>
Date: Fri, 27 Aug 2021 22:06:56 +0300
cmd/haredoc: implement TTY output
Signed-off-by: Alexey Yerin <yyp@disroot.org>
Diffstat:
2 files changed, 295 insertions(+), 3 deletions(-)
diff --git a/cmd/haredoc/main.ha b/cmd/haredoc/main.ha
@@ -9,6 +9,7 @@ use io;
use os;
use path;
use strings;
+use unix::tty;
type format = enum {
HARE,
@@ -29,8 +30,7 @@ type context = struct {
};
export fn main() void = {
- // TODO: Use format::TTY by default if stdout is a tty
- let fmt = format::HARE;
+ let fmt = if (tty::isatty(os::stdout)) format::TTY else format::HARE;
let template = true;
const help: [_]getopt::help = [
"reads and formats Hare documentation",
@@ -130,7 +130,7 @@ fn scan(path: str) (ast::subunit | error) = {
fn emit(ctx: *context) (void | error) = switch (ctx.format) {
format::HARE => emit_hare(ctx),
- format::TTY => abort(), // TODO
+ format::TTY => emit_tty(ctx),
format::HTML => emit_html(ctx)?,
format::GEMTEXT => abort(), // TODO
};
diff --git a/cmd/haredoc/tty.ha b/cmd/haredoc/tty.ha
@@ -0,0 +1,292 @@
+use fmt;
+use hare::ast;
+use hare::lex;
+use hare::unparse;
+use io;
+use os;
+use strings;
+use strio;
+
+// Formats output as Hare source code (prototypes) with syntax highlighting
+fn emit_tty(ctx: *context) (void | error) = {
+ const summary = ctx.summary;
+ // XXX: Should we emit the dependencies, too?
+ for (let i = 0z; i < len(summary.types); i += 1) {
+ details_tty(summary.types[i])?;
+ };
+ for (let i = 0z; i < len(summary.errors); i += 1) {
+ details_tty(summary.errors[i])?;
+ };
+ for (let i = 0z; i < len(summary.globals); i += 1) {
+ details_tty(summary.globals[i])?;
+ };
+ for (let i = 0z; i < len(summary.funcs); i += 1) {
+ details_tty(summary.funcs[i])?;
+ };
+};
+
+fn details_tty(decl: ast::decl) (void | error) = {
+ const iter = strings::tokenize(decl.docs, "\n");
+ for (true) match (strings::next_token(&iter)) {
+ s: str => if (len(s) != 0) {
+ fmt::printfln("\x1b[38;5;246m" "//{}" "\x1b[0m", s)?;
+ },
+ void => break,
+ };
+
+ unparse_tty(os::stdout, decl)?;
+ fmt::print("\n\n")?;
+ return;
+};
+
+// Forked from [[hare::unparse]]
+fn unparse_tty(out: *io::stream, d: ast::decl) (size | io::error) = {
+ let n = 0z;
+ match (d.decl) {
+ g: []ast::decl_global => {
+ n += fmt::fprint(out, "\x1b[34m")?;
+ n += fmt::fprint(out,
+ if (g[0].is_const) "def " else "let ")?;
+ n += fmt::fprint(out, "\x1b[0m")?;
+ for (let i = 0z; i < len(g); i += 1) {
+ if (len(g[i].symbol) != 0) {
+ n += fmt::fprintf(out,
+ "\x1b[33m" "@symbol(\"{}\") " "\x1b[0m",
+ g[i].symbol)?;
+ };
+ n += unparse::ident(out, g[i].ident)?;
+ n += fmt::fprint(out, ": ")?;
+ n += type_tty(out, 0, g[i]._type)?;
+ if (i + 1 < len(g)) {
+ n += fmt::fprint(out, ", ")?;
+ };
+ };
+ },
+ t: []ast::decl_type => {
+ n += fmt::fprint(out, "\x1b[34m" "type " "\x1b[0m")?;
+ for (let i = 0z; i < len(t); i += 1) {
+ n += unparse::ident(out, t[i].ident)?;
+ n += fmt::fprint(out, " = ")?;
+ n += type_tty(out, 0, t[i]._type)?;
+ if (i + 1 < len(t)) {
+ n += fmt::fprint(out, ", ")?;
+ };
+ };
+ },
+ f: ast::decl_func => {
+ n += fmt::fprint(out, "\x1b[33m")?;
+ n += fmt::fprint(out, switch (f.attrs) {
+ ast::fndecl_attrs::NONE => "",
+ ast::fndecl_attrs::FINI => "@fini ",
+ ast::fndecl_attrs::INIT => "@init ",
+ ast::fndecl_attrs::TEST => "@test ",
+ })?;
+ n += fmt::fprint(out, "\x1b[0m")?;
+
+ let p = f.prototype.repr as ast::func_type;
+ if (p.attrs & ast::func_attrs::NORETURN != 0) {
+ n += fmt::fprint(out,
+ "\x1b[33m" "@noreturn " "\x1b[0m")?;
+ };
+ if (len(f.symbol) != 0) {
+ n += fmt::fprintf(out,
+ "\x1b[33m" "@symbol(\"{}\") " "\x1b[0m",
+ f.symbol)?;
+ };
+ n += fmt::fprint(out, "\x1b[34m" "fn " "\x1b[0m")?;
+ n += unparse::ident(out, f.ident)?;
+ n += prototype_tty(out, 0,
+ f.prototype.repr as ast::func_type)?;
+ },
+ };
+ n += fmt::fprint(out, ";")?;
+ return n;
+};
+
+fn prototype_tty(
+ out: *io::stream,
+ indent: size,
+ t: ast::func_type,
+) (size | io::error) = {
+ let n = 0z;
+ n += fmt::fprint(out, "(")?;
+ for (let i = 0z; i < len(t.params); i += 1) {
+ let param = t.params[i];
+ n += fmt::fprintf(out, "{}: ",
+ if (len(param.name) == 0) "_" else param.name)?;
+ n += type_tty(out, indent, *param._type)?;
+ if (i + 1 == len(t.params)
+ && t.variadism == ast::variadism::HARE) {
+ n += fmt::fprintf(out, "...")?;
+ };
+ if (i + 1 < len(t.params)) {
+ n += fmt::fprint(out, ", ")?;
+ };
+ };
+ if (t.variadism == ast::variadism::C) {
+ n += fmt::fprint(out, ", ...")?;
+ };
+ n += fmt::fprint(out, ") ")?;
+ n += type_tty(out, indent, *t.result)?;
+ return n;
+};
+
+// newline() and builtin_type() are from cmd/haredoc/html.ha
+
+// Forked from [[hare::unparse]]
+fn struct_union_type_tty(
+ out: *io::stream,
+ indent: size,
+ t: ast::_type,
+) (size | io::error) = {
+ let z = 0z;
+ let membs = match (t.repr) {
+ st: ast::struct_type => {
+ z += fmt::fprint(out,
+ "\x1b[36m" "struct" "\x1b[0m" " {")?;
+ st: []ast::struct_member;
+ },
+ ut: ast::union_type => {
+ z += fmt::fprint(out,
+ "\x1b[36m" "union" "\x1b[0m" " {")?;
+ ut: []ast::struct_member;
+ },
+ };
+
+ indent += 1z;
+ for (let i = 0z; i < len(membs); i += 1) {
+ z += newline(out, indent)?;
+
+ z += match (membs[i]._offset) {
+ null => 0z,
+ ex: *ast::expr => fmt::fprint(out, "\x1b[33m" "@offset(")?
+ + unparse::expr(out, indent, *ex)?
+ + fmt::fprint(out, ") \x1b[0m")?,
+ };
+
+ z += match (membs[i].member) {
+ se: ast::struct_embedded => type_tty(out, indent, *se)?,
+ sa: ast::struct_alias => unparse::ident(out, sa)?,
+ sf: ast::struct_field => {
+ fmt::fprintf(out, "{}: ", sf.name)?
+ + type_tty(out, indent, *sf._type)?;
+ },
+ };
+
+ z += fmt::fprint(out, ",")?;
+ };
+
+ indent -= 1;
+ z += newline(out, indent)?;
+ z += fmt::fprint(out, "}")?;
+ return z;
+};
+
+// Forked from [[hare::unparse]]
+fn type_tty(
+ out: *io::stream,
+ indent: size,
+ t: ast::_type,
+) (size | io::error) = {
+ let n = 0z;
+ if (t.flags & ast::type_flags::CONST != 0
+ && !(t.repr is ast::func_type)) {
+ n += fmt::fprint(out, "\x1b[36m" "const " "\x1b[0m")?;
+ };
+ if (t.flags & ast::type_flags::ERROR != 0) {
+ if (t.repr is ast::builtin_type) {
+ n += fmt::fprint(out, "\x1b[36m" "!" "\x1b[0m")?;
+ } else {
+ n += fmt::fprint(out, "!")?;
+ };
+ };
+
+ match (t.repr) {
+ a: ast::alias_type => {
+ if (a.unwrap) {
+ n += fmt::fprint(out, "...")?;
+ };
+ n += unparse::ident(out, a.ident)?;
+ },
+ b: ast::builtin_type => {
+ n += fmt::fprintf(out, "\x1b[36m" "{}" "\x1b[0m",
+ builtin_type(b))?;
+ },
+ e: ast::enum_type => {
+ n += fmt::fprint(out, "\x1b[36m" "enum " "\x1b[0m")?;
+ if (e.storage != ast::builtin_type::INT) {
+ n += fmt::fprintf(out,
+ "\x1b[36m" "{}" "\x1b[0m",
+ builtin_type(e.storage))?;
+ };
+ n += fmt::fprint(out, "{")?;
+ indent += 1;
+ for (let i = 0z; i < len(e.values); i += 1) {
+ n += newline(out, indent)?;
+ let value = e.values[i];
+ n += fmt::fprint(out, value.name)?;
+ match (value.value) {
+ null => void,
+ e: *ast::expr => {
+ n += fmt::fprint(out, " = ")?;
+ n += unparse::expr(out, indent, *e)?;
+ },
+ };
+ n += fmt::fprint(out, ",")?;
+ };
+ indent -= 1;
+ n += newline(out, indent)?;
+ n += fmt::fprint(out, "}")?;
+ },
+ f: ast::func_type => {
+ if (f.attrs & ast::func_attrs::NORETURN != 0) {
+ n += fmt::fprint(out,
+ "\x1b[33m" "@noreturn " "\x1b[0m")?;
+ };
+ n += fmt::fprint(out, "\x1b[34m" "fn" "\x1b[0m")?;
+ n += prototype_tty(out, indent, f)?;
+ },
+ l: ast::list_type => {
+ n += fmt::fprint(out, "[")?;
+ n += match (l.length) {
+ ast::len_slice => 0,
+ ast::len_unbounded => fmt::fprint(out, "*")?,
+ ast::len_contextual => fmt::fprint(out, "_")?,
+ e: *ast::expr => unparse::expr(out, indent, *e)?,
+ };
+ n += fmt::fprint(out, "]")?;
+ n += type_tty(out, indent, *l.members)?;
+ },
+ p: ast::pointer_type => {
+ if (p.flags & ast::pointer_flags::NULLABLE != 0) {
+ n += fmt::fprint(out,
+ "\x1b[36m" "nullable " "\x1b[0m")?;
+ };
+ n += fmt::fprint(out, "*")?;
+ n += type_tty(out, indent, *p.referent)?;
+ },
+ ast::struct_type => n += struct_union_type_tty(out, indent, t)?,
+ ast::union_type => n += struct_union_type_tty(out, indent, t)?,
+ t: ast::tagged_type => {
+ n += fmt::fprint(out, "(")?;
+ for (let i = 0z; i < len(t); i += 1) {
+ n += type_tty(out, indent, *t[i])?;
+ if (i + 1 < len(t)) {
+ n += fmt::fprint(out, " | ")?;
+ };
+ };
+ n += fmt::fprint(out, ")")?;
+ },
+ t: ast::tuple_type => {
+ n += fmt::fprint(out, "(")?;
+ for (let i = 0z; i < len(t); i += 1) {
+ n += type_tty(out, indent, *t[i])?;
+ if (i + 1 < len(t)) {
+ n += fmt::fprint(out, ", ")?;
+ };
+ };
+ n += fmt::fprint(out, ")")?;
+ },
+ };
+ return n;
+};