commit 85c9efa37a8dd880f5eacbf3995bede80a81f797
parent c7ac5d879554e12bc5caa75f190c3c4bf9622f2b
Author: Sebastian <sebastian@sebsite.pw>
Date: Sat, 30 Sep 2023 23:36:48 -0400
haredoc: refactor to use unparse::syn
Signed-off-by: Sebastian <sebastian@sebsite.pw>
Diffstat:
7 files changed, 202 insertions(+), 1156 deletions(-)
diff --git a/cmd/haredoc/doc/color.ha b/cmd/haredoc/doc/color.ha
@@ -2,96 +2,107 @@
// (c) Hare authors <https://harelang.org>
use ascii;
-use fmt;
+use hare::unparse;
use io;
use os;
use regex;
use strings;
-// Syntax type
-type syn = enum uint {
- NORMAL,
- COMMENT,
- PRIMARY,
- CONSTANT,
- FUNCTION,
- GLOBAL,
- TYPEDEF,
- SECONDARY,
- KEYWORD,
- TYPE,
- ATTRIBUTE,
- OPERATOR,
- PUNCTUATION,
- STRING,
- NUMBER,
-};
-
// Colors/Renditions with defaults; SGR parameters for ANSI escape sequences.
-let COLORS: [_](str, str) = [
- ("normal" , "0"),
- ("comment" , "1"),
- ("primary" , "0"),
- ("constant" , "0"),
- ("function" , "0"),
- ("global" , "0"),
- ("typedef" , "0"),
- ("secondary" , "0"),
- ("keyword" , "94"),
- ("type" , "96"),
- ("attribute" , "33"),
- ("operator" , "1"),
- ("punctuation" , "0"),
- ("string" , "91"),
- ("number" , "95"),
+let colors: [_]str = [
+ "0", // ident
+ "1", // comment
+ "0", // constant
+ "0", // function
+ "0", // global
+ "0", // typedef
+ "0", // import_alias
+ "0", // secondary
+ "94", // keyword
+ "96", // type
+ "33", // attribute
+ "1", // operator
+ "0", // punctuation
+ "91", // rune_string
+ "95", // number
+ "0", // label
];
-fn init_colors() void = {
+let normal_color = "0";
+let primary_color = "0";
+
+fn init_colors() (void | error) = {
const env_colors = os::tryenv("HAREDOC_COLORS", "");
- const expr = regex::compile(`([a-z][a-z]*)=(_|[0-9;]*)`)!;
+ const expr = regex::compile(`([a-z_]+)=(_|[0-9;]*)`)!;
defer regex::finish(&expr);
const matches = regex::findall(&expr, env_colors);
defer regex::result_freeall(matches);
for (let i = 0z; i < len(matches); i += 1) :colors {
- const (k, v) = (matches[i][1].content, matches[i][2].content);
-
- let idx = 0z;
- for (let j = 0z; j < len(COLORS); j += 1) {
- if (k == COLORS[j].0) {
- idx = j;
- break;
- } else if (j == len(COLORS) - 1) {
- fmt::fatalf(
- "Error parsing HAREDOC_COLORS, "
- "invalid key '{}'", k,
- );
- };
- };
-
- if (v == "_") {
- COLORS[idx] = if (k == "normal") (k, "0") else (k, v);
- continue;
- };
+ let (k, v) = (matches[i][1].content, matches[i][2].content);
if (v == "") {
continue;
};
- COLORS[idx] = (k, v);
+ let idx = 0z;
+ let out: *str = switch (k) {
+ case "ident" =>
+ yield &colors[unparse::synkind::IDENT];
+ case "comment" =>
+ yield &colors[unparse::synkind::COMMENT];
+ case "constant" =>
+ yield &colors[unparse::synkind::CONSTANT];
+ case "function" =>
+ yield &colors[unparse::synkind::FUNCTION];
+ case "global" =>
+ yield &colors[unparse::synkind::GLOBAL];
+ case "typedef" =>
+ yield &colors[unparse::synkind::TYPEDEF];
+ case "import_alias" =>
+ yield &colors[unparse::synkind::IMPORT_ALIAS];
+ case "secondary" =>
+ yield &colors[unparse::synkind::SECONDARY];
+ case "keyword" =>
+ yield &colors[unparse::synkind::KEYWORD];
+ case "type" =>
+ yield &colors[unparse::synkind::TYPE];
+ case "attribute" =>
+ yield &colors[unparse::synkind::ATTRIBUTE];
+ case "operator" =>
+ yield &colors[unparse::synkind::OPERATOR];
+ case "punctuation" =>
+ yield &colors[unparse::synkind::PUNCTUATION];
+ case "rune_string" =>
+ yield &colors[unparse::synkind::RUNE_STRING];
+ case "number" =>
+ yield &colors[unparse::synkind::NUMBER];
+ case "label" =>
+ yield &colors[unparse::synkind::LABEL];
+ case "normal" =>
+ yield &normal_color;
+ case "primary" =>
+ yield &primary_color;
+ case =>
+ return k: haredoc_colors_error;
+ };
+
+ *out = if (v == "_" && k != "normal") "0" else v;
};
};
-fn render(h: io::handle, syntax: syn) (size | io::error) = {
- if (COLORS[syntax].1 == "_") {
- syntax = switch (syntax) {
- case syn::CONSTANT, syn::FUNCTION, syn::GLOBAL, syn::TYPEDEF =>
- yield if (COLORS[syn::PRIMARY].1 == "_") syn::NORMAL
- else syn::PRIMARY;
- case =>
- yield syn::NORMAL;
- };
+fn color(kind: unparse::synkind) str = {
+ if (colors[kind] != "_") {
+ return colors[kind];
+ };
+ switch (kind) {
+ case unparse::synkind::CONSTANT,
+ unparse::synkind::FUNCTION,
+ unparse::synkind::GLOBAL,
+ unparse::synkind::TYPEDEF =>
+ return primary_color;
+ case =>
+ return normal_color;
};
- return fmt::fprintf(h, "\x1b[0;{}m", COLORS[syntax].1)?;
};
diff --git a/cmd/haredoc/doc/hare.ha b/cmd/haredoc/doc/hare.ha
@@ -4,7 +4,6 @@
use bufio;
use fmt;
use hare::ast;
-use hare::lex;
use hare::module;
use hare::unparse;
use io;
@@ -114,87 +113,7 @@ fn details_hare(ctx: *context, decl: ast::decl) (void | error) = {
};
};
- unparse_hare(ctx.out, decl)?;
+ unparse::decl(ctx.out, &unparse::syn_wrap, decl)?;
fmt::fprintln(ctx.out)?;
return;
};
-
-// Forked from [[hare::unparse]]
-fn unparse_hare(out: io::handle, d: ast::decl) (size | io::error) = {
- let n = 0z;
- match (d.decl) {
- case let g: []ast::decl_global =>
- n += fmt::fprint(out,
- if (g[0].is_const) "const " else "let ")?;
- for (let i = 0z; i < len(g); i += 1) {
- if (len(g[i].symbol) != 0) {
- n += fmt::fprintf(out,
- "@symbol(\"{}\") ", g[i].symbol)?;
- };
- n += unparse::ident(out, g[i].ident)?;
- match (g[i]._type) {
- case null =>
- yield;
- case let ty: *ast::_type =>
- n += fmt::fprint(out, ": ")?;
- n += unparse::_type(out,
- &unparse::syn_nowrap, *ty)?;
- };
- if (i + 1 < len(g)) {
- n += fmt::fprint(out, ", ")?;
- };
- };
- case let t: []ast::decl_type =>
- n += fmt::fprint(out, "type ")?;
- for (let i = 0z; i < len(t); i += 1) {
- n += unparse::ident(out, t[i].ident)?;
- n += fmt::fprint(out, " = ")?;
- n += unparse::_type(out,
- &unparse::syn_nowrap, t[i]._type)?;
- if (i + 1 < len(t)) {
- n += fmt::fprint(out, ", ")?;
- };
- };
- case let c: []ast::decl_const =>
- n += fmt::fprint(out, "def ")?;
- for (let i = 0z; i < len(c); i += 1) {
- n += unparse::ident(out, c[i].ident)?;
- n += fmt::fprint(out, ": ")?;
- match (c[i]._type) {
- case null =>
- yield;
- case let ty: *ast::_type =>
- n += fmt::fprint(out, ": ")?;
- n += unparse::_type(out,
- &unparse::syn_nowrap, *ty)?;
- };
- if (i + 1 < len(c)) {
- n += fmt::fprint(out, ", ")?;
- };
- };
- case let f: ast::decl_func =>
- n += fmt::fprint(out, switch (f.attrs) {
- case ast::fndecl_attrs::NONE =>
- yield "";
- case ast::fndecl_attrs::FINI =>
- yield "@fini ";
- case ast::fndecl_attrs::INIT =>
- yield "@init ";
- case ast::fndecl_attrs::TEST =>
- yield "@test ";
- })?;
- let p = f.prototype.repr as ast::func_type;
- if (len(f.symbol) != 0) {
- n += fmt::fprintf(out, "@symbol(\"{}\") ",
- f.symbol)?;
- };
- n += fmt::fprint(out, "fn ")?;
- n += unparse::ident(out, f.ident)?;
- n += unparse::prototype(&unparse::context {
- out = out,
- ...
- }, &unparse::syn_nowrap, f.prototype.repr as ast::func_type)?;
- };
- n += fmt::fprint(out, ";")?;
- return n;
-};
diff --git a/cmd/haredoc/doc/html.ha b/cmd/haredoc/doc/html.ha
@@ -5,16 +5,12 @@
use encoding::utf8;
use fmt;
use hare::ast;
-use hare::ast::{variadism};
-use hare::lex;
-use hare::module;
use hare::parse::doc;
use hare::unparse;
use io;
use memio;
use net::ip;
use net::uri;
-use os;
use path;
use strings;
@@ -166,32 +162,6 @@ export fn emit_html(ctx: *context) (void | error) = {
};
};
-fn comment_html(out: io::handle, s: str) (size | io::error) = {
- // TODO: handle [[references]]
- let z = fmt::fprint(out, "<span class='comment'>//")?;
- z += html_escape(out, s)?;
- z += fmt::fprint(out, "</span><br>")?;
- return z;
-};
-
-fn docs_html(out: io::handle, s: str, indent: size) (size | io::error) = {
- const iter = strings::tokenize(s, "\n");
- let z = 0z;
- for (true) match (strings::next_token(&iter)) {
- case let s: str =>
- if (!(strings::peek_token(&iter) is void)) {
- z += comment_html(out, s)?;
- for (let i = 0z; i < indent; i += 1) {
- z += fmt::fprint(out, "\t")?;
- };
- };
- case void =>
- break;
- };
-
- return z;
-};
-
fn tocentries(
out: io::handle,
decls: []ast::decl,
@@ -213,59 +183,12 @@ fn tocentries(
lname)?;
undoc = true;
};
- tocentry(out, decls[i])?;
+ unparse::decl(out, &syn_centry, decls[i])?;
};
fmt::fprint(out, "</pre>")?;
return;
};
-fn tocentry(out: io::handle, decl: ast::decl) (void | error) = {
- fmt::fprintf(out, "{} ",
- match (decl.decl) {
- case ast::decl_func =>
- yield "fn";
- case []ast::decl_type =>
- yield "type";
- case []ast::decl_const =>
- yield "const";
- case []ast::decl_global =>
- yield "let";
- })?;
- fmt::fprintf(out, "<a href='#")?;
- unparse::ident(out, decl_ident(decl))?;
- fmt::fprintf(out, "'>")?;
- unparse::ident(out, decl_ident(decl))?;
- fmt::fprint(out, "</a>")?;
-
- match (decl.decl) {
- case let t: []ast::decl_type => void;
- case let g: []ast::decl_global =>
- let g = g[0];
- match (g._type) {
- case null =>
- yield;
- case let ty: *ast::_type =>
- fmt::fprint(out, ": ")?;
- type_html(out, 0, *ty, true)?;
- };
- case let c: []ast::decl_const =>
- let c = c[0];
- match (c._type) {
- case null =>
- yield;
- case let ty: *ast::_type =>
- fmt::fprint(out, ": ")?;
- type_html(out, 0, *ty, true)?;
- };
- case let f: ast::decl_func =>
- prototype_html(out, 0,
- f.prototype.repr as ast::func_type,
- true)?;
- };
- fmt::fprintln(out, ";")?;
- return;
-};
-
fn details(ctx: *context, decl: ast::decl) (void | error) = {
fmt::fprintln(ctx.out, "<section class='member'>")?;
fmt::fprint(ctx.out, "<h4 id='")?;
@@ -295,7 +218,7 @@ fn details(ctx: *context, decl: ast::decl) (void | error) = {
};
fmt::fprintln(ctx.out, "<pre class='decl'>")?;
- unparse_html(ctx.out, decl)?;
+ unparse::decl(ctx.out, &syn_html, decl)?;
fmt::fprintln(ctx.out, "</pre>")?;
if (len(decl.docs) != 0) {
@@ -416,409 +339,63 @@ fn markup_html(ctx: *context, in: io::handle) (void | error) = {
return;
};
-// Forked from [[hare::unparse]]
-fn unparse_html(out: io::handle, d: ast::decl) (size | io::error) = {
- let n = 0z;
- match (d.decl) {
- case let c: []ast::decl_const =>
- n += fmt::fprintf(out, "<span class='keyword'>def</span> ")?;
- for (let i = 0z; i < len(c); i += 1) {
- n += unparse::ident(out, c[i].ident)?;
- match (c[i]._type) {
- case null =>
- yield;
- case let ty: *ast::_type =>
- n += fmt::fprint(out, ": ")?;
- n += type_html(out, 0, *ty, false)?;
- };
- if (i + 1 < len(c)) {
- n += fmt::fprint(out, ", ")?;
- };
- };
- case let g: []ast::decl_global =>
- n += fmt::fprintf(out, "<span class='keyword'>{}</span>",
- if (g[0].is_const) "const " else "let ")?;
- for (let i = 0z; i < len(g); i += 1) {
- n += unparse::ident(out, g[i].ident)?;
- match (g[i]._type) {
- case null =>
- yield;
- case let ty: *ast::_type =>
- n += fmt::fprint(out, ": ")?;
- n += type_html(out, 0, *ty, false)?;
- };
- if (i + 1 < len(g)) {
- n += fmt::fprint(out, ", ")?;
- };
- };
- case let t: []ast::decl_type =>
- n += fmt::fprint(out, "<span class='keyword'>type</span> ")?;
- for (let i = 0z; i < len(t); i += 1) {
- n += unparse::ident(out, t[i].ident)?;
- n += fmt::fprint(out, " = ")?;
- n += type_html(out, 0, t[i]._type, false)?;
- if (i + 1 < len(t)) {
- n += fmt::fprint(out, ", ")?;
- };
- };
- case let f: ast::decl_func =>
- n += fmt::fprint(out, switch (f.attrs) {
- case ast::fndecl_attrs::NONE =>
- yield "";
- case ast::fndecl_attrs::FINI =>
- yield "@fini ";
- case ast::fndecl_attrs::INIT =>
- yield "@init ";
- case ast::fndecl_attrs::TEST =>
- yield "@test ";
- })?;
- let p = f.prototype.repr as ast::func_type;
- n += fmt::fprint(out, "<span class='keyword'>fn</span> ")?;
- n += unparse::ident(out, f.ident)?;
- n += prototype_html(out, 0,
- f.prototype.repr as ast::func_type,
- false)?;
- };
- n += fmt::fprint(out, ";")?;
- return n;
-};
-
-fn enum_html(
- out: io::handle,
- indent: size,
- t: ast::enum_type
+fn syn_centry(
+ ctx: *unparse::context,
+ s: str,
+ kind: unparse::synkind,
) (size | io::error) = {
let z = 0z;
-
- z += fmt::fprint(out, "<span class='type'>enum</span> ")?;
- if (t.storage != ast::builtin_type::INT) {
- z += fmt::fprintf(out, "<span class='type'>{}</span> ",
- unparse::builtin_type(t.storage))?;
- };
- z += fmt::fprintln(out, "{")?;
- indent += 1;
- for (let i = 0z; i < len(t.values); i += 1) {
- for (let i = 0z; i < indent; i += 1) {
- z += fmt::fprint(out, "\t")?;
- };
- const val = t.values[i];
- let wrotedocs = false;
- if (val.docs != "") {
- // Check if comment should go above or next to field
- if (multiline_comment(val.docs)) {
- z += docs_html(out, val.docs, indent)?;
- wrotedocs = true;
- };
- };
-
- z += fmt::fprint(out, val.name)?;
-
- match (val.value) {
- case null => void;
- case let expr: *ast::expr =>
- z += fmt::fprint(out, " = ")?;
- z += unparse::expr(out, &unparse::syn_nowrap, *expr)?;
- };
-
- z += fmt::fprint(out, ",")?;
-
- if (val.docs != "" && !wrotedocs) {
- z += fmt::fprint(out, " ")?;
- z += docs_html(out, val.docs, 0)?;
- } else {
- z += fmt::fprintln(out)?;
- };
- };
- indent -= 1;
- for (let i = 0z; i < indent; i += 1) {
- z += fmt::fprint(out, "\t")?;
- };
- z += newline(out, indent)?;
- z += fmt::fprint(out, "}")?;
- return z;
-};
-
-fn struct_union_html(
- out: io::handle,
- indent: size,
- t: ast::_type,
- brief: bool,
-) (size | io::error) = {
- let z = 0z;
- let members = match (t.repr) {
- case let t: ast::struct_type =>
- z += fmt::fprint(out, "<span class='keyword'>struct</span>")?;
- if (t.packed) {
- z += fmt::fprint(out, " @packed")?;
- };
- z += fmt::fprint(out, " {")?;
- yield t.members: []ast::struct_member;
- case let t: ast::union_type =>
- z += fmt::fprint(out, "<span class='keyword'>union</span> {")?;
- yield t: []ast::struct_member;
- };
-
- indent += 1;
- for (let i = 0z; i < len(members); i += 1) {
- const member = members[i];
-
- z += newline(out, indent)?;
- if (member.docs != "" && !brief) {
- z += docs_html(out, member.docs, indent)?;
- };
- match (member._offset) {
- case null => void;
- case let expr: *ast::expr =>
- z += fmt::fprint(out, "@offset(")?;
- z += unparse::expr(out, &unparse::syn_nowrap, *expr)?;
- z += fmt::fprint(out, ") ")?;
- };
-
- match (member.member) {
- case let f: ast::struct_field =>
- z += fmt::fprintf(out, "{}: ", f.name)?;
- z += type_html(out, indent, *f._type, brief)?;
- case let embed: ast::struct_embedded =>
- z += type_html(out, indent, *embed, brief)?;
- case let indent: ast::struct_alias =>
- z += unparse::ident(out, indent)?;
- };
- z += fmt::fprint(out, ",")?;
+ switch (kind) {
+ case unparse::synkind::CONSTANT,
+ unparse::synkind::FUNCTION,
+ unparse::synkind::GLOBAL,
+ unparse::synkind::TYPEDEF =>
+ z += fmt::fprint(ctx.out, "<a href='#")?;
+ z += html_escape(ctx.out, s)?;
+ z += fmt::fprint(ctx.out, "'>")?;
+ z += html_escape(ctx.out, s)?;
+ z += fmt::fprint(ctx.out, "</a>")?;
+ ctx.linelen += len(s);
+ return z;
+ case =>
+ return syn_html(ctx, s, kind);
};
-
- indent -= 1;
- z += newline(out, indent)?;
- z += fmt::fprint(out, "}")?;
-
- return z;
};
-fn type_html(
- out: io::handle,
- indent: size,
- _type: ast::_type,
- brief: bool,
+fn syn_html(
+ ctx: *unparse::context,
+ s: str,
+ kind: unparse::synkind,
) (size | io::error) = {
- if (brief) {
- let buf = memio::dynamic();
- defer io::close(&buf)!;
- unparse::_type(&buf, &unparse::syn_nowrap, _type)?;
- return html_escape(out, memio::string(&buf)!)?;
- };
-
- // TODO: More detailed formatter which can find aliases nested deeper in
- // other types and highlight more keywords, like const
let z = 0z;
-
- if (_type.flags & ast::type_flag::CONST != 0
- && !(_type.repr is ast::func_type)) {
- z += fmt::fprint(out, "<span class='keyword'>const</span> ")?;
+ const span = switch (kind) {
+ case unparse::synkind::COMMENT =>
+ const stack = ctx.stack as *unparse::stack;
+ if (stack.cur is *ast::decl) {
+ // doc comment is unparsed separately later
+ return 0z;
+ };
+ z += fmt::fprint(ctx.out, "<span class='comment'>")?;
+ yield true;
+ case unparse::synkind::KEYWORD =>
+ z += fmt::fprint(ctx.out, "<span class='keyword'>")?;
+ yield true;
+ case unparse::synkind::TYPE =>
+ z += fmt::fprint(ctx.out, "<span class='type'>")?;
+ yield true;
+ case =>
+ yield false;
+ };
+
+ z += html_escape(ctx.out, s)?;
+ ctx.linelen += len(s);
+
+ if (span) {
+ z += fmt::fprint(ctx.out, "</span>")?;
};
-
- if (_type.flags & ast::type_flag::ERROR != 0) {
- if (_type.repr is ast::builtin_type) {
- z += fmt::fprint(out, "<span class='type'>!</span>")?;
- } else {
- z += fmt::fprint(out, "!")?;
- };
- };
-
- match (_type.repr) {
- case let a: ast::alias_type =>
- if (a.unwrap) {
- z += fmt::fprint(out, "...")?;
- };
- z += unparse::ident(out, a.ident)?;
- case let t: ast::builtin_type =>
- z += fmt::fprintf(out, "<span class='type'>{}</span>",
- unparse::builtin_type(t))?;
- case let t: ast::tagged_type =>
- // rough estimate of current line length
- let linelen: size = z + (indent + 1) * 8;
- z = 0;
- linelen += fmt::fprint(out, "(")?;
- for (let i = 0z; i < len(t); i += 1) {
- linelen += type_html(out, indent, *t[i], brief)?;
- if (i + 1 == len(t)) {
- break;
- };
- linelen += fmt::fprint(out, " |")?;
- // use 72 instead of 80 to give a bit of leeway for long
- // type names
- if (linelen > 72) {
- z += linelen;
- linelen = (indent + 1) * 8;
- z += fmt::fprintln(out)?;
- for (let i = 0z; i < indent; i += 1) {
- z += fmt::fprint(out, "\t")?;
- };
- } else {
- linelen += fmt::fprint(out, " ")?;
- };
- };
- z += linelen;
- z += fmt::fprint(out, ")")?;
- case let t: ast::tuple_type =>
- // rough estimate of current line length
- let linelen: size = z + (indent + 1) * 8;
- z = 0;
- linelen += fmt::fprint(out, "(")?;
- for (let i = 0z; i < len(t); i += 1) {
- linelen += type_html(out, indent, *t[i], brief)?;
- if (i + 1 == len(t)) {
- break;
- };
- linelen += fmt::fprint(out, ",")?;
- // use 72 instead of 80 to give a bit of leeway for long
- // type names
- if (linelen > 72) {
- z += linelen;
- linelen = (indent + 1) * 8;
- z += fmt::fprintln(out)?;
- for (let i = 0z; i < indent; i += 1) {
- z += fmt::fprint(out, "\t")?;
- };
- } else {
- linelen += fmt::fprint(out, " ")?;
- };
- };
- z += linelen;
- z += fmt::fprint(out, ")")?;
- case let t: ast::pointer_type =>
- if (t.flags & ast::pointer_flag::NULLABLE != 0) {
- z += fmt::fprint(out, "<span class='type'>nullable</span> ")?;
- };
- z += fmt::fprint(out, "*")?;
- z += type_html(out, indent, *t.referent, brief)?;
- case let t: ast::func_type =>
- z += fmt::fprint(out, "<span class='keyword'>fn</span>(")?;
- for (let i = 0z; i < len(t.params); i += 1) {
- const param = t.params[i];
- z += fmt::fprintf(out, "{}: ",
- if (len(param.name) == 0) "_" else param.name)?;
- z += type_html(out, indent, *param._type, brief)?;
-
- if (i + 1 == len(t.params)
- && t.variadism == ast::variadism::HARE) {
- // TODO: Highlight that as well
- z += fmt::fprint(out, "...")?;
- };
- if (i + 1 < len(t.params)) {
- z += fmt::fprint(out, ", ")?;
- };
- };
- if (t.variadism == ast::variadism::C) {
- z += fmt::fprint(out, ", ...")?;
- };
- z += fmt::fprint(out, ") ")?;
- z += type_html(out, indent, *t.result, brief)?;
- case let t: ast::enum_type =>
- z += enum_html(out, indent, t)?;
- case let t: ast::list_type =>
- z += fmt::fprint(out, "[")?;
- match (t.length) {
- case let expr: *ast::expr =>
- z += unparse::expr(out, &unparse::syn_nowrap, *expr)?;
- case ast::len_slice =>
- z += 0;
- case ast::len_unbounded =>
- z += fmt::fprintf(out, "*")?;
- case ast::len_contextual =>
- z += fmt::fprintf(out, "_")?;
- };
- z += fmt::fprint(out, "]")?;
-
- z += type_html(out, indent, *t.members, brief)?;
- case let t: ast::struct_type =>
- z += struct_union_html(out, indent, _type, brief)?;
- case let t: ast::union_type =>
- z += struct_union_html(out, indent, _type, brief)?;
- };
-
return z;
};
-fn prototype_html(
- out: io::handle,
- indent: size,
- t: ast::func_type,
- brief: bool,
-) (size | io::error) = {
- let n = 0z;
- n += fmt::fprint(out, "(")?;
-
- // estimate length of prototype to determine if it should span multiple
- // lines
- const linelen = if (len(t.params) == 0 || brief) {
- yield 0z; // If no parameters or brief, only use one line.
- } else {
- let linelen = indent * 8 + 5;
- linelen += if (len(t.params) != 0) len(t.params) * 3 - 1 else 0;
- for (let i = 0z; i < len(t.params); i += 1) {
- const param = t.params[i];
- linelen += unparse::_type(io::empty,
- &unparse::syn_nowrap, *param._type)?;
- linelen += if (param.name == "") 1 else len(param.name);
- };
- switch (t.variadism) {
- case variadism::NONE => void;
- case variadism::HARE =>
- linelen += 3;
- case variadism::C =>
- linelen += 5;
- };
- linelen += unparse::_type(io::empty,
- &unparse::syn_nowrap, *t.result)?;
- yield linelen;
- };
-
- // use 72 instead of 80 to give a bit of leeway for preceding text
- if (linelen > 72) {
- indent += 1;
- for (let i = 0z; i < len(t.params); i += 1) {
- const param = t.params[i];
- n += newline(out, indent)?;
- n += fmt::fprintf(out, "{}: ",
- if (param.name == "") "_" else param.name)?;
- n += type_html(out, indent, *param._type, brief)?;
- if (i + 1 == len(t.params)
- && t.variadism == variadism::HARE) {
- n += fmt::fprint(out, "...")?;
- } else {
- n += fmt::fprint(out, ",")?;
- };
- };
- if (t.variadism == variadism::C) {
- n += newline(out, indent)?;
- n += fmt::fprint(out, "...")?;
- };
- indent -= 1;
- n += newline(out, indent)?;
- } else for (let i = 0z; i < len(t.params); i += 1) {
- const param = t.params[i];
- if (!brief) {
- n += fmt::fprintf(out, "{}: ",
- if (param.name == "") "_" else param.name)?;
- };
- n += type_html(out, indent, *param._type, brief)?;
- if (i + 1 == len(t.params)) {
- switch (t.variadism) {
- case variadism::NONE => void;
- case variadism::HARE =>
- n += fmt::fprint(out, "...")?;
- case variadism::C =>
- n += fmt::fprint(out, ", ...")?;
- };
- } else {
- n += fmt::fprint(out, ", ")?;
- };
- };
-
- n += fmt::fprint(out, ") ")?;
- n += type_html(out, indent, *t.result, brief)?;
- return n;
-};
-
fn breadcrumb(ident: ast::ident) str = {
if (len(ident) == 0) {
return "";
diff --git a/cmd/haredoc/doc/sort.ha b/cmd/haredoc/doc/sort.ha
@@ -5,10 +5,15 @@ use hare::ast;
use sort;
use strings;
-// Sorts declarations by removing unexported declarations, moving undocumented
-// declarations to the end, sorting by identifier, and ensuring that only one
-// member is present in each declaration (so that "let x: int = 10, y: int = 20"
-// becomes two declarations: "let x: int = 10; let y: int = 20;").
+// Sorts declarations by:
+// - removing unexported declarations,
+// - setting the "exported" field of all remaining declarations to false, so the
+// "export" keyword isn't unparsed,
+// - moving undocumented declarations to the end,
+// - sorting by identifier,
+// - removing the initializer from globals and the body from functions,
+// - ensuring that only one member is present in each declaration:
+// "let x: int, y: int;" becomes two declarations: "let x: int; let y: int;".
export fn sort_decls(decls: []ast::decl) summary = {
let sorted = summary { ... };
@@ -20,7 +25,19 @@ export fn sort_decls(decls: []ast::decl) summary = {
match (decl.decl) {
case let f: ast::decl_func =>
- append(sorted.funcs, decl);
+ append(sorted.funcs, ast::decl {
+ exported = false,
+ start = decl.start,
+ end = decl.end,
+ decl = ast::decl_func {
+ symbol = f.symbol,
+ ident = f.ident,
+ prototype = f.prototype,
+ body = void,
+ attrs = f.attrs,
+ },
+ docs = decl.docs,
+ });
case let t: []ast::decl_type =>
for (let j = 0z; j < len(t); j += 1) {
let bucket = &sorted.types;
@@ -28,7 +45,7 @@ export fn sort_decls(decls: []ast::decl) summary = {
bucket = &sorted.errors;
};
append(bucket, ast::decl {
- exported = true,
+ exported = false,
start = decl.start,
end = decl.end,
decl = alloc([t[j]]),
@@ -38,7 +55,7 @@ export fn sort_decls(decls: []ast::decl) summary = {
case let c: []ast::decl_const =>
for (let j = 0z; j < len(c); j += 1) {
append(sorted.constants, ast::decl {
- exported = true,
+ exported = false,
start = decl.start,
end = decl.end,
decl = alloc([c[j]]),
@@ -48,10 +65,17 @@ export fn sort_decls(decls: []ast::decl) summary = {
case let g: []ast::decl_global =>
for (let j = 0z; j < len(g); j += 1) {
append(sorted.globals, ast::decl {
- exported = true,
+ exported = false,
start = decl.start,
end = decl.end,
- decl = alloc([g[j]]),
+ decl = alloc([ast::decl_global {
+ is_const = g[j].is_const,
+ is_threadlocal = g[j].is_threadlocal,
+ symbol = g[j].symbol,
+ ident = g[j].ident,
+ _type = g[j]._type,
+ init = null,
+ }]),
docs = decl.docs,
});
};
diff --git a/cmd/haredoc/doc/tty.ha b/cmd/haredoc/doc/tty.ha
@@ -5,31 +5,32 @@ use ascii;
use bufio;
use fmt;
use hare::ast;
-use hare::ast::{variadism};
-use hare::lex;
use hare::unparse;
use io;
use memio;
use os;
use strings;
+use types;
let firstline: bool = true;
// Formats output as Hare source code (prototypes) with syntax highlighting
export fn emit_tty(ctx: *context) (void | error) = {
- init_colors();
+ init_colors()?;
const summary = ctx.summary;
match (ctx.readme) {
case let readme: io::file =>
- for (true) match (bufio::read_line(readme)?) {
- case io::EOF =>
- break;
- case let b: []u8 =>
- defer free(b);
+ let rbuf: [os::BUFSZ]u8 = [0...];
+ let readme = bufio::init(readme, rbuf, []);
+ let sc = bufio::newscanner(&readme, types::SIZE_MAX);
+ defer bufio::finish(&sc);
+ for (true) match (bufio::scan_line(&sc)?) {
+ case io::EOF => break;
+ case let s: const str =>
firstline = false;
- insert(b[0], ' ');
- comment_tty(ctx.out, strings::fromutf8(b)!)?;
+ fmt::fprintfln(ctx.out, "\x1b[{}m" "// {}" "\x1b[m",
+ color(unparse::synkind::COMMENT), s)?;
};
case void => void;
};
@@ -57,14 +58,12 @@ export fn emit_tty(ctx: *context) (void | error) = {
fn emit_submodules_tty(ctx: *context) (void | error) = {
if (len(ctx.submods) != 0) {
fmt::fprintln(ctx.out)?;
+ fmt::fprintf(ctx.out, "\x1b[{}m",
+ color(unparse::synkind::COMMENT))?;
if (len(ctx.ident) == 0) {
- render(ctx.out, syn::COMMENT)?;
fmt::fprintln(ctx.out, "// Modules")?;
- render(ctx.out, syn::NORMAL)?;
} else {
- render(ctx.out, syn::COMMENT)?;
fmt::fprintln(ctx.out, "// Submodules")?;
- render(ctx.out, syn::NORMAL)?;
};
for (let i = 0z; i < len(ctx.submods); i += 1) {
let submodule = if (len(ctx.ident) != 0) {
@@ -76,521 +75,33 @@ fn emit_submodules_tty(ctx: *context) (void | error) = {
};
defer free(submodule);
- render(ctx.out, syn::COMMENT)?;
fmt::fprintfln(ctx.out, "// - [[{}]]", submodule)?;
- render(ctx.out, syn::NORMAL)?;
};
};
};
-fn comment_tty(out: io::handle, s: str) (size | io::error) = {
- let n = 0z;
- n += render(out, syn::COMMENT)?;
- n += fmt::fprintfln(out, "//{}", s)?;
- n += render(out, syn::NORMAL)?;
- return n;
-};
-
-fn docs_tty(out: io::handle, s: str, indent: size) (size | io::error) = {
- const iter = strings::tokenize(s, "\n");
- let z = 0z;
- for (true) match (strings::next_token(&iter)) {
- case let s: str =>
- if (!(strings::peek_token(&iter) is void)) {
- z += comment_tty(out, s)?;
- for (let i = 0z; i < indent; i += 1) {
- z += fmt::fprint(out, "\t")?;
- };
- };
- case void =>
- break;
- };
-
- return z;
-};
-
-fn isws(s: str) bool = {
- const iter = strings::iter(s);
- for (true) {
- match (strings::next(&iter)) {
- case let r: rune =>
- if (!ascii::isspace(r)) {
- return false;
- };
- case void =>
- break;
- };
- };
- return true;
-};
-
fn details_tty(ctx: *context, decl: ast::decl) (void | error) = {
if (len(decl.docs) == 0 && !ctx.show_undocumented) {
return;
};
+ fmt::fprint(ctx.out, "\x1b[m")?; // reset styling
if (!firstline) {
fmt::fprintln(ctx.out)?;
};
firstline = false;
- docs_tty(ctx.out, decl.docs, 0)?;
- unparse_tty(ctx.out, decl)?;
+ unparse::decl(ctx.out, &syn_tty, decl)?;
fmt::fprintln(ctx.out)?;
};
-// Forked from [[hare::unparse]]
-fn unparse_tty(out: io::handle, d: ast::decl) (size | io::error) = {
- let n = 0z;
- match (d.decl) {
- case let g: []ast::decl_global =>
- n += render(out, syn::KEYWORD)?;
- n += fmt::fprint(out, if (g[0].is_const) "const " else "let ")?;
- for (let i = 0z; i < len(g); i += 1) {
- if (len(g[i].symbol) != 0) {
- n += render(out, syn::ATTRIBUTE)?;
- n += fmt::fprintf(out, "@symbol(")?;
- n += render(out, syn::STRING)?;
- n += fmt::fprintf(out, `"{}"`, g[i].symbol)?;
- n += render(out, syn::ATTRIBUTE)?;
- n += fmt::fprintf(out, ") ")?;
- n += render(out, syn::NORMAL)?;
- };
- n += render(out, syn::GLOBAL)?;
- n += unparse::ident(out, g[i].ident)?;
- match (g[i]._type) {
- case null =>
- yield;
- case let ty: *ast::_type =>
- n += render(out, syn::PUNCTUATION)?;
- n += fmt::fprint(out, ": ")?;
- n += type_tty(out, 0, *ty)?;
- };
- if (i + 1 < len(g)) {
- n += render(out, syn::PUNCTUATION)?;
- n += fmt::fprint(out, ", ")?;
- };
- n += render(out, syn::NORMAL)?;
- };
- case let c: []ast::decl_const =>
- n += render(out, syn::KEYWORD)?;
- n += fmt::fprintf(out, "def ")?;
- for (let i = 0z; i < len(c); i += 1) {
- n += render(out, syn::CONSTANT)?;
- n += unparse::ident(out, c[i].ident)?;
- n += render(out, syn::PUNCTUATION)?;
- match (c[i]._type) {
- case null =>
- yield;
- case let ty: *ast::_type =>
- n += render(out, syn::PUNCTUATION)?;
- n += fmt::fprint(out, ": ")?;
- n += type_tty(out, 0, *ty)?;
- };
- if (i + 1 < len(c)) {
- n += render(out, syn::PUNCTUATION)?;
- n += fmt::fprint(out, ", ")?;
- };
- };
- case let t: []ast::decl_type =>
- n += render(out, syn::KEYWORD)?;
- n += fmt::fprint(out, "type ")?;
- for (let i = 0z; i < len(t); i += 1) {
- n += render(out, syn::TYPEDEF)?;
- n += unparse::ident(out, t[i].ident)?;
- n += render(out, syn::PUNCTUATION)?;
- n += fmt::fprint(out, " = ")?;
- n += type_tty(out, 0, t[i]._type)?;
- if (i + 1 < len(t)) {
- n += render(out, syn::PUNCTUATION)?;
- n += fmt::fprint(out, ", ")?;
- };
- };
- case let f: ast::decl_func =>
- n += render(out, syn::ATTRIBUTE)?;
- n += fmt::fprint(out, switch (f.attrs) {
- case ast::fndecl_attrs::NONE =>
- yield "";
- case ast::fndecl_attrs::FINI =>
- yield "@fini ";
- case ast::fndecl_attrs::INIT =>
- yield "@init ";
- case ast::fndecl_attrs::TEST =>
- yield "@test ";
- })?;
- n += render(out, syn::NORMAL)?;
-
- let p = f.prototype.repr as ast::func_type;
- if (len(f.symbol) != 0) {
- n += render(out, syn::ATTRIBUTE)?;
- n += fmt::fprintf(out, "@symbol(")?;
- n += render(out, syn::STRING)?;
- n += fmt::fprintf(out, `"{}"`, f.symbol)?;
- n += render(out, syn::ATTRIBUTE)?;
- n += fmt::fprintf(out, ") ")?;
- n += render(out, syn::NORMAL)?;
- };
- n += render(out, syn::KEYWORD)?;
- n += fmt::fprint(out, "fn ")?;
- n += render(out, syn::FUNCTION)?;
- n += unparse::ident(out, f.ident)?;
- n += fmt::fprint(out, "\x1b[0m")?;
- n += prototype_tty(out, 0,
- f.prototype.repr as ast::func_type)?;
- };
- n += render(out, syn::PUNCTUATION)?;
- n += fmt::fprint(out, ";")?;
- return n;
-};
-
-fn prototype_tty(
- out: io::handle,
- indent: size,
- t: ast::func_type,
+fn syn_tty(
+ ctx: *unparse::context,
+ s: str,
+ kind: unparse::synkind,
) (size | io::error) = {
- let n = 0z;
- n += render(out, syn::PUNCTUATION)?;
- n += fmt::fprint(out, "(")?;
-
- let typenames: []str = [];
- // TODO: https://todo.sr.ht/~sircmpwn/hare/581
- if (len(t.params) > 0) {
- typenames = alloc([""...], len(t.params));
- };
- defer strings::freeall(typenames);
- let retname = "";
- defer free(retname);
-
- // estimate length of prototype to determine if it should span multiple
- // lines
- const linelen = if (len(t.params) == 0) {
- let strm = memio::dynamic();
- defer io::close(&strm)!;
- type_tty(&strm, indent, *t.result)?;
- retname = strings::dup(memio::string(&strm)!);
- yield 0z; // only use one line if there's no parameters
- } else {
- let strm = memio::dynamic();
- defer io::close(&strm)!;
- let linelen = indent * 8 + 5;
- linelen += if (len(t.params) != 0) len(t.params) * 3 - 1 else 0;
- for (let i = 0z; i < len(t.params); i += 1) {
- const param = t.params[i];
- linelen += unparse::_type(&strm,
- &unparse::syn_nowrap, *param._type)?;
- typenames[i] = strings::dup(memio::string(&strm)!);
- linelen += if (param.name == "") 1 else len(param.name);
- memio::reset(&strm);
- };
- switch (t.variadism) {
- case variadism::NONE => void;
- case variadism::HARE =>
- linelen += 3;
- case variadism::C =>
- linelen += 5;
- };
- linelen += type_tty(&strm, indent, *t.result)?;
- retname = strings::dup(memio::string(&strm)!);
- yield linelen;
- };
-
- // use 72 instead of 80 to give a bit of leeway for preceding text
- if (linelen > 72) {
- indent += 1;
- for (let i = 0z; i < len(t.params); i += 1) {
- const param = t.params[i];
- n += newline(out, indent)?;
- n += render(out, syn::SECONDARY)?;
- n += fmt::fprint(out,
- if (param.name == "") "_" else param.name)?;
- n += render(out, syn::PUNCTUATION)?;
- n += fmt::fprint(out, ": ")?;
- n += render(out, syn::TYPE)?;
- n += fmt::fprint(out, typenames[i])?;
- if (i + 1 == len(t.params)
- && t.variadism == variadism::HARE) {
- n += render(out, syn::OPERATOR)?;
- n += fmt::fprint(out, "...")?;
- } else {
- n += render(out, syn::PUNCTUATION)?;
- n += fmt::fprint(out, ",")?;
- };
- };
- if (t.variadism == variadism::C) {
- n += newline(out, indent)?;
- n += render(out, syn::OPERATOR)?;
- n += fmt::fprint(out, "...")?;
- };
- indent -= 1;
- n += newline(out, indent)?;
- } else for (let i = 0z; i < len(t.params); i += 1) {
- const param = t.params[i];
- n += render(out, syn::SECONDARY)?;
- n += fmt::fprint(out,
- if (param.name == "") "_" else param.name)?;
- n += render(out, syn::PUNCTUATION)?;
- n += fmt::fprint(out, ": ")?;
- n += render(out, syn::TYPE)?;
- n += fmt::fprint(out, typenames[i])?;
- if (i + 1 == len(t.params)) {
- switch (t.variadism) {
- case variadism::NONE => void;
- case variadism::HARE =>
- n += render(out, syn::OPERATOR)?;
- n += fmt::fprint(out, "...")?;
- case variadism::C =>
- n += render(out, syn::PUNCTUATION)?;
- n += fmt::fprint(out, ", ")?;
- n += render(out, syn::OPERATOR)?;
- n += fmt::fprint(out, "...")?;
- };
- } else {
- n += render(out, syn::PUNCTUATION)?;
- n += fmt::fprint(out, ", ")?;
- };
- };
-
- n += render(out, syn::PUNCTUATION)?;
- n += fmt::fprint(out, ")", retname)?;
- return n;
-};
-
-// Forked from [[hare::unparse]]
-fn struct_union_type_tty(
- out: io::handle,
- indent: size,
- t: ast::_type,
-) (size | io::error) = {
- let n = 0z;
- let membs = match (t.repr) {
- case let st: ast::struct_type =>
- n += render(out, syn::TYPE)?;
- n += fmt::fprint(out, "struct")?;
- if (st.packed) {
- n += render(out, syn::ATTRIBUTE)?;
- n += fmt::fprint(out, " @packed")?;
- };
- n += render(out, syn::PUNCTUATION)?;
- n += fmt::fprint(out, " {")?;
- yield st.members: []ast::struct_member;
- case let ut: ast::union_type =>
- n += render(out, syn::TYPE)?;
- n += fmt::fprint(out, "union")?;
- n += render(out, syn::PUNCTUATION)?;
- n += fmt::fprint(out, " {")?;
- yield ut: []ast::struct_member;
- };
-
- indent += 1z;
- for (let i = 0z; i < len(membs); i += 1) {
- n += newline(out, indent)?;
- if (membs[i].docs != "") {
- n += docs_tty(out, membs[i].docs, indent)?;
- };
-
- match (membs[i]._offset) {
- case null => void;
- case let ex: *ast::expr =>
- n += render(out, syn::ATTRIBUTE)?;
- n += fmt::fprint(out, "@offset(")?;
- n += render(out, syn::NUMBER)?;
- n += unparse::expr(out, &unparse::syn_nowrap, *ex)?;
- n += render(out, syn::ATTRIBUTE)?;
- n += fmt::fprint(out, ")")?;
- n += render(out, syn::NORMAL)?;
- };
-
- match (membs[i].member) {
- case let se: ast::struct_embedded =>
- n += type_tty(out, indent, *se)?;
- case let sa: ast::struct_alias =>
- n += unparse::ident(out, sa)?;
- case let sf: ast::struct_field =>
- n += render(out, syn::SECONDARY)?;
- n += fmt::fprint(out, sf.name)?;
- n += render(out, syn::PUNCTUATION)?;
- n += fmt::fprint(out, ": ")?;
- n += type_tty(out, indent, *sf._type)?;
- };
-
- n += render(out, syn::PUNCTUATION)?;
- n += fmt::fprint(out, ",")?;
- };
-
- indent -= 1;
- n += newline(out, indent)?;
- n += render(out, syn::PUNCTUATION)?;
- n += fmt::fprint(out, "}")?;
- return n;
-};
-
-// Forked from [[hare::unparse]]
-fn type_tty(
- out: io::handle,
- indent: size,
- t: ast::_type,
-) (size | io::error) = {
- let n = 0z;
- if (t.flags & ast::type_flag::CONST != 0
- && !(t.repr is ast::func_type)) {
- n += render(out, syn::TYPE)?;
- n += fmt::fprint(out, "const ")?;
- };
- if (t.flags & ast::type_flag::ERROR != 0) {
- n += render(out, syn::OPERATOR)?;
- n += fmt::fprint(out, "!")?;
- };
-
- match (t.repr) {
- case let a: ast::alias_type =>
- if (a.unwrap) {
- n += render(out, syn::OPERATOR)?;
- n += fmt::fprint(out, "...")?;
- };
- n += render(out, syn::TYPE)?;
- n += unparse::ident(out, a.ident)?;
- case let b: ast::builtin_type =>
- n += render(out, syn::TYPE)?;
- n += fmt::fprintf(out, "{}", unparse::builtin_type(b))?;
- case let e: ast::enum_type =>
- n += render(out, syn::TYPE)?;
- n += fmt::fprint(out, "enum ")?;
- if (e.storage != ast::builtin_type::INT) {
- n += fmt::fprintf(out,
- "{} ", unparse::builtin_type(e.storage))?;
- };
- n += render(out, syn::PUNCTUATION)?;
- n += fmt::fprintln(out, "{")?;
- indent += 1;
- for (let i = 0z; i < len(e.values); i += 1) {
- for (let i = 0z; i < indent; i += 1) {
- n += fmt::fprint(out, "\t")?;
- };
- let value = e.values[i];
- let wrotedocs = false;
- if (value.docs != "") {
- // Check if comment should go above or next to
- // field
- if (multiline_comment(value.docs)) {
- n += docs_tty(out, value.docs, indent)?;
- wrotedocs = true;
- };
- };
- n += render(out, syn::SECONDARY)?;
- n += fmt::fprint(out, value.name)?;
- match (value.value) {
- case null => void;
- case let e: *ast::expr =>
- n += render(out, syn::OPERATOR)?;
- n += fmt::fprint(out, " = ")?;
- n += render(out, syn::NORMAL)?;
- n += unparse::expr(out,
- &unparse::syn_nowrap, *e)?;
- };
- n += render(out, syn::PUNCTUATION)?;
- n += fmt::fprint(out, ",")?;
- if (value.docs != "" && !wrotedocs) {
- n += fmt::fprint(out, " ")?;
- n += docs_tty(out, value.docs, 0)?;
- } else {
- n += fmt::fprintln(out)?;
- };
- };
- indent -= 1;
- for (let i = 0z; i < indent; i += 1) {
- n += fmt::fprint(out, "\t")?;
- };
- n += render(out, syn::PUNCTUATION)?;
- n += fmt::fprint(out, "}")?;
- case let f: ast::func_type =>
- n += render(out, syn::TYPE)?;
- n += fmt::fprint(out, "fn")?;
- n += prototype_tty(out, indent, f)?;
- case let l: ast::list_type =>
- n += render(out, syn::OPERATOR)?;
- n += fmt::fprint(out, "[")?;
- match (l.length) {
- case ast::len_slice => void;
- case ast::len_unbounded =>
- n += fmt::fprint(out, "*")?;
- case ast::len_contextual =>
- n += fmt::fprint(out, "_")?;
- case let e: *ast::expr =>
- n += unparse::expr(out, &unparse::syn_nowrap, *e)?;
- };
- n += render(out, syn::OPERATOR)?;
- n += fmt::fprint(out, "]")?;
- n += type_tty(out, indent, *l.members)?;
- case let p: ast::pointer_type =>
- if (p.flags & ast::pointer_flag::NULLABLE != 0) {
- n += render(out, syn::TYPE)?;
- n += fmt::fprint(out, "nullable ")?;
- };
- n += render(out, syn::OPERATOR)?;
- n += fmt::fprint(out, "*")?;
- n += type_tty(out, indent, *p.referent)?;
- case ast::struct_type =>
- n += struct_union_type_tty(out, indent, t)?;
- case ast::union_type =>
- n += struct_union_type_tty(out, indent, t)?;
- case let t: ast::tagged_type =>
- // rough estimate of current line length
- let linelen: size = n + (indent + 1) * 8;
- n = 0;
- n += render(out, syn::PUNCTUATION)?;
- linelen += fmt::fprint(out, "(")?;
- for (let i = 0z; i < len(t); i += 1) {
- linelen += type_tty(out, indent, *t[i])?;
- if (i + 1 == len(t)) {
- break;
- };
- n += render(out, syn::PUNCTUATION)?;
- linelen += fmt::fprint(out, " |")?;
- // use 72 instead of 80 to give a bit of leeway for long
- // type names
- if (linelen > 72) {
- n += linelen;
- linelen = (indent + 1) * 8;
- n += fmt::fprintln(out)?;
- for (let i = 0z; i <= indent; i += 1) {
- n += fmt::fprint(out, "\t")?;
- };
- } else {
- linelen += fmt::fprint(out, " ")?;
- };
- };
- n += linelen;
- n += render(out, syn::PUNCTUATION)?;
- n += fmt::fprint(out, ")")?;
- case let t: ast::tuple_type =>
- // rough estimate of current line length
- let linelen: size = n + (indent + 1) * 8;
- n = 0;
- n += render(out, syn::PUNCTUATION)?;
- linelen += fmt::fprint(out, "(")?;
- for (let i = 0z; i < len(t); i += 1) {
- linelen += type_tty(out, indent, *t[i])?;
- if (i + 1 == len(t)) {
- break;
- };
- n += render(out, syn::PUNCTUATION)?;
- linelen += fmt::fprint(out, ",")?;
- // use 72 instead of 80 to give a bit of leeway for long
- // type names
- if (linelen > 72) {
- n += linelen;
- linelen = (indent + 1) * 8;
- n += fmt::fprintln(out)?;
- for (let i = 0z; i <= indent; i += 1) {
- n += fmt::fprint(out, "\t")?;
- };
- } else {
- linelen += fmt::fprint(out, " ")?;
- };
- };
- n += linelen;
- n += render(out, syn::PUNCTUATION)?;
- n += fmt::fprint(out, ")")?;
- };
- return n;
+ let z = fmt::fprintf(ctx.out, "\x1b[{}m", color(kind))?;
+ z += unparse::syn_wrap(ctx, s, kind)?;
+ z += fmt::fprint(ctx.out, "\x1b[m")?;
+ return z;
};
diff --git a/cmd/haredoc/doc/types.ha b/cmd/haredoc/doc/types.ha
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-only
// (c) Hare authors <https://harelang.org>
+use fmt;
use fs;
use hare::ast;
use hare::lex;
@@ -9,7 +10,12 @@ use hare::parse;
use io;
use os::exec;
-export type error = !(lex::error | parse::error | io::error | module::error | exec::error | fs::error);
+export type haredoc_colors_error = !str;
+
+export type error = !(lex::error | parse::error | io::error | module::error |
+ exec::error | fs::error | haredoc_colors_error);
+
+def HAREDOC_COLORS_ERROR_MSG = "Error parsing HAREDOC_COLORS: invalid key";
export fn strerror(err: error) str = {
match (err) {
@@ -21,6 +27,17 @@ export fn strerror(err: error) str = {
return io::strerror(err);
case let err: module::error =>
return module::strerror(err);
+ case let err: exec::error =>
+ return exec::strerror(err);
+ case let err: fs::error =>
+ return fs::strerror(err);
+ case let err: haredoc_colors_error =>
+ let buf: [128]u8 = [0...];
+ if (len(HAREDOC_COLORS_ERROR_MSG) + len(err) + 3 > len(buf)) {
+ return HAREDOC_COLORS_ERROR_MSG;
+ };
+ return fmt::bsprintf(buf, "{} '{}'",
+ HAREDOC_COLORS_ERROR_MSG, err);
};
};
diff --git a/cmd/haredoc/doc/util.ha b/cmd/haredoc/doc/util.ha
@@ -11,19 +11,6 @@ use sort;
use sort::cmp;
use strings;
-// Forked from [[hare::unparse]].
-fn newline(out: io::handle, indent: size) (size | io::error) = {
- let n = 0z;
- n += fmt::fprint(out, "\n")?;
- for (let i = 0z; i < indent; i += 1) {
- n += fmt::fprint(out, "\t")?;
- };
- return n;
-};
-
-fn multiline_comment(s: str) bool =
- strings::byteindex(s, '\n') as size != len(s) - 1;
-
fn trim_comment(s: str) str = {
let trimmed = memio::dynamic();
let tok = strings::tokenize(s, "\n");