commit c2f0840a0ba79b36f4f03aa41cf31efce57821ab
parent ceca1b49a652baaeb5aa13db801e53964ed1a417
Author: Sebastian <sebastian@sebsite.pw>
Date: Tue, 17 Oct 2023 22:30:11 -0400
haredoc: show initializer functions for types
For instance, `haredoc memio::stream` also shows all bufio functions
which return a stream.
This doesn't implement anything for HTML, since we plan to drop the HTML
backend anyway.
I opted to use the following rules here:
- Types only show initializer functions if they aren't error types and
if they aren't aliases of void.
- A function is an "initializer" function if it returns the type, a
pointer to or slice of the type, or a tagged union containing the
type.
- Initializer functions are shown whether or not the type is documented.
My rationale here is 1. if you're specifically looking for docs on one
type, initializer functions will always be helpful to see, and 2. the
example of fs::iterator given in the original ticket now has docs, and
this is true of lots of types that the user still isn't expected to
directly poke at, such as strings::tokenizer.
- Only initializer functions are shown, not functions which take in the
type. The original ticket mentioned that fs::iterator should show
fs::next, and I initially implemented this by checking if the first
parameter matched the type (using the same rules as for when checking
a function return type), but this caused way too many additional
functions to appear, the vast majority of which aren't actually
helpful. For example, docs for path::buffer would show nearly every
path:: function, rather than just path::init. If there are certain
functions that are important to know about for a type, they're most
likely already explicitly documented anyways.
Implements: https://todo.sr.ht/~sircmpwn/hare/421
Signed-off-by: Sebastian <sebastian@sebsite.pw>
Diffstat:
M | cmd/haredoc/main.ha | | | 109 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- |
1 file changed, 104 insertions(+), 5 deletions(-)
diff --git a/cmd/haredoc/main.ha b/cmd/haredoc/main.ha
@@ -154,17 +154,26 @@ fn doc(name: str, cmd: *getopt::command) (void | error) = {
ast::imports_finish(u.imports);
append(decls, u.decls...);
};
- let new: []ast::decl = [];
+
+ let matching: []ast::decl = [];
+ let notmatching: []ast::decl = [];
for (let i = 0z; i < len(decls); i += 1) {
if (has_decl(decls[i], id[len(id) - 1])) {
- append(new, decls[i]);
+ append(matching, decls[i]);
} else {
- ast::decl_finish(decls[i]);
+ append(notmatching, decls[i]);
};
};
+ get_init_funcs(&matching, ¬matching);
+
+ for (let i = 0z; i < len(notmatching); i += 1) {
+ ast::decl_finish(notmatching[i]);
+ };
+ free(notmatching);
free(decls);
- decls = new;
- if (len(new) == 0) {
+ decls = matching;
+
+ if (len(matching) == 0) {
if (modpath == "") {
const id = unparse::identstr(id);
fmt::fatalf("Could not find {}", id);
@@ -372,3 +381,93 @@ fn has_decl(decl: ast::decl, name: str) bool = {
assert(parseident("") is void);
assert(parseident("::") is void);
};
+
+fn get_init_funcs(matching: *[]ast::decl, notmatching: *[]ast::decl) void = {
+ if (len(matching) != 1) {
+ return;
+ };
+ let ident = match (matching[0].decl) {
+ case let d: []ast::decl_type =>
+ if (len(d) != 1 || len(d[0].ident) != 1) {
+ return;
+ };
+ if (d[0]._type.flags & ast::type_flag::ERROR != 0) {
+ return;
+ };
+ match (d[0]._type.repr) {
+ case let repr: ast::builtin_type =>
+ if (repr == ast::builtin_type::VOID) {
+ return;
+ };
+ case => void;
+ };
+ yield d[0].ident;
+ case =>
+ return;
+ };
+
+ for (let i = 0z; i < len(notmatching); i += 1) {
+ let _type = match (notmatching[i].decl) {
+ case let d: []ast::decl_const =>
+ yield match (d[0]._type) {
+ case let t: *ast::_type =>
+ yield t;
+ case null =>
+ continue;
+ };
+ case let d: []ast::decl_global =>
+ yield match (d[0]._type) {
+ case let t: *ast::_type =>
+ yield t;
+ case null =>
+ continue;
+ };
+ case let d: ast::decl_func =>
+ let _type = d.prototype.repr as ast::func_type;
+ yield _type.result;
+ case =>
+ continue;
+ };
+
+ if (is_init_type(ident, _type)) {
+ append(matching, notmatching[i]);
+ delete(notmatching[i]);
+ i -= 1;
+ };
+ };
+};
+
+fn is_init_type(ident: ast::ident, _type: *ast::_type) bool = {
+ let type_ident = match (_type.repr) {
+ case let repr: ast::alias_type =>
+ yield repr.ident;
+ case let repr: ast::list_type =>
+ if (!(repr.length is ast::len_slice)) {
+ return false;
+ };
+ yield match (repr.members.repr) {
+ case let repr: ast::alias_type =>
+ yield repr.ident;
+ case =>
+ return false;
+ };
+ case let repr: ast::pointer_type =>
+ yield match (repr.referent.repr) {
+ case let repr: ast::alias_type =>
+ yield repr.ident;
+ case =>
+ return false;
+ };
+ case let repr: ast::tagged_type =>
+ for (let i = 0z; i < len(repr); i += 1) {
+ if (is_init_type(ident, repr[i])) {
+ return true;
+ };
+ };
+ return false;
+ case =>
+ return false;
+ };
+
+ return ast::ident_eq(ident, type_ident);
+};