docs.cpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. // Generates Documentation
  2. gb_global int print_entity_kind_ordering[Entity_Count] = {
  3. /*Invalid*/ -1,
  4. /*Constant*/ 0,
  5. /*Variable*/ 1,
  6. /*TypeName*/ 4,
  7. /*Procedure*/ 2,
  8. /*ProcGroup*/ 3,
  9. /*Builtin*/ -1,
  10. /*ImportName*/ -1,
  11. /*LibraryName*/ -1,
  12. /*Nil*/ -1,
  13. /*Label*/ -1,
  14. };
  15. gb_global char const *print_entity_names[Entity_Count] = {
  16. /*Invalid*/ "",
  17. /*Constant*/ "constants",
  18. /*Variable*/ "variables",
  19. /*TypeName*/ "types",
  20. /*Procedure*/ "procedures",
  21. /*ProcGroup*/ "proc_group",
  22. /*Builtin*/ "",
  23. /*ImportName*/ "import names",
  24. /*LibraryName*/ "library names",
  25. /*Nil*/ "",
  26. /*Label*/ "",
  27. };
  28. gb_internal GB_COMPARE_PROC(cmp_entities_for_printing) {
  29. GB_ASSERT(a != nullptr);
  30. GB_ASSERT(b != nullptr);
  31. Entity *x = *cast(Entity **)a;
  32. Entity *y = *cast(Entity **)b;
  33. int res = 0;
  34. if (x->pkg != y->pkg) {
  35. if (x->pkg == nullptr) {
  36. return -1;
  37. }
  38. if (y->pkg == nullptr) {
  39. return +1;
  40. }
  41. res = string_compare(x->pkg->name, y->pkg->name);
  42. if (res != 0) {
  43. return res;
  44. }
  45. }
  46. int ox = print_entity_kind_ordering[x->kind];
  47. int oy = print_entity_kind_ordering[y->kind];
  48. res = ox - oy;
  49. if (res != 0) {
  50. return res;
  51. }
  52. res = string_compare(x->token.string, y->token.string);
  53. return res;
  54. }
  55. gb_internal GB_COMPARE_PROC(cmp_ast_package_by_name) {
  56. GB_ASSERT(a != nullptr);
  57. GB_ASSERT(b != nullptr);
  58. AstPackage *x = *cast(AstPackage **)a;
  59. AstPackage *y = *cast(AstPackage **)b;
  60. return string_compare(x->name, y->name);
  61. }
  62. #include "docs_format.cpp"
  63. #include "docs_writer.cpp"
  64. gb_internal void print_doc_line(i32 indent, String const &data) {
  65. while (indent --> 0) {
  66. gb_printf("\t");
  67. }
  68. gb_file_write(gb_file_get_standard(gbFileStandard_Output), data.text, data.len);
  69. gb_printf("\n");
  70. }
  71. gb_internal void print_doc_line(i32 indent, char const *fmt, ...) {
  72. while (indent --> 0) {
  73. gb_printf("\t");
  74. }
  75. va_list va;
  76. va_start(va, fmt);
  77. gb_printf_va(fmt, va);
  78. va_end(va);
  79. gb_printf("\n");
  80. }
  81. gb_internal void print_doc_line_no_newline(i32 indent, String const &data) {
  82. while (indent --> 0) {
  83. gb_printf("\t");
  84. }
  85. gb_file_write(gb_file_get_standard(gbFileStandard_Output), data.text, data.len);
  86. }
  87. gb_internal bool print_doc_comment_group_string(i32 indent, CommentGroup *g) {
  88. if (g == nullptr) {
  89. return false;
  90. }
  91. isize len = 0;
  92. for_array(i, g->list) {
  93. String comment = g->list[i].string;
  94. len += comment.len;
  95. len += 1; // for \n
  96. }
  97. if (len <= g->list.count) {
  98. return false;
  99. }
  100. isize count = 0;
  101. for_array(i, g->list) {
  102. String comment = g->list[i].string;
  103. String original_comment = comment;
  104. bool slash_slash = false;
  105. if (comment[1] == '/') {
  106. slash_slash = true;
  107. comment.text += 2;
  108. comment.len -= 2;
  109. } else if (comment[1] == '*') {
  110. comment.text += 2;
  111. comment.len -= 4;
  112. }
  113. // Ignore the first space
  114. if (comment.len > 0 && comment[0] == ' ') {
  115. comment.text += 1;
  116. comment.len -= 1;
  117. }
  118. if (slash_slash) {
  119. if (string_starts_with(comment, str_lit("+"))) {
  120. continue;
  121. }
  122. if (string_starts_with(comment, str_lit("@("))) {
  123. continue;
  124. }
  125. }
  126. if (slash_slash) {
  127. print_doc_line(indent, comment);
  128. count += 1;
  129. } else {
  130. isize pos = 0;
  131. for (; pos < comment.len; pos++) {
  132. isize end = pos;
  133. for (; end < comment.len; end++) {
  134. if (comment[end] == '\n') {
  135. break;
  136. }
  137. }
  138. String line = substring(comment, pos, end);
  139. pos = end;
  140. String trimmed_line = string_trim_whitespace(line);
  141. if (trimmed_line.len == 0) {
  142. if (count == 0) {
  143. continue;
  144. }
  145. }
  146. /*
  147. * Remove comments with
  148. * styles
  149. * like this
  150. */
  151. if (string_starts_with(line, str_lit("* "))) {
  152. line = substring(line, 2, line.len);
  153. }
  154. print_doc_line(indent, line);
  155. count += 1;
  156. }
  157. }
  158. }
  159. if (count > 0) {
  160. print_doc_line(0, "");
  161. return true;
  162. }
  163. return false;
  164. }
  165. gb_internal void print_doc_expr(Ast *expr) {
  166. gbString s = nullptr;
  167. if (build_context.cmd_doc_flags & CmdDocFlag_Short) {
  168. s = expr_to_string_shorthand(expr);
  169. } else {
  170. s = expr_to_string(expr);
  171. }
  172. gb_file_write(gb_file_get_standard(gbFileStandard_Output), s, gb_string_length(s));
  173. gb_string_free(s);
  174. }
  175. gb_internal void print_doc_package(CheckerInfo *info, AstPackage *pkg) {
  176. if (pkg == nullptr) {
  177. return;
  178. }
  179. print_doc_line(0, "package %.*s", LIT(pkg->name));
  180. for_array(i, pkg->files) {
  181. AstFile *f = pkg->files[i];
  182. if (f->pkg_decl) {
  183. GB_ASSERT(f->pkg_decl->kind == Ast_PackageDecl);
  184. print_doc_comment_group_string(1, f->pkg_decl->PackageDecl.docs);
  185. }
  186. }
  187. if (pkg->scope != nullptr) {
  188. auto entities = array_make<Entity *>(heap_allocator(), 0, pkg->scope->elements.entries.count);
  189. defer (array_free(&entities));
  190. for (auto const &entry : pkg->scope->elements) {
  191. Entity *e = entry.value;
  192. switch (e->kind) {
  193. case Entity_Invalid:
  194. case Entity_Builtin:
  195. case Entity_Nil:
  196. case Entity_Label:
  197. continue;
  198. case Entity_Constant:
  199. case Entity_Variable:
  200. case Entity_TypeName:
  201. case Entity_Procedure:
  202. case Entity_ProcGroup:
  203. case Entity_ImportName:
  204. case Entity_LibraryName:
  205. // Fine
  206. break;
  207. }
  208. array_add(&entities, e);
  209. }
  210. gb_sort_array(entities.data, entities.count, cmp_entities_for_printing);
  211. bool show_docs = (build_context.cmd_doc_flags & CmdDocFlag_Short) == 0;
  212. EntityKind curr_entity_kind = Entity_Invalid;
  213. for_array(i, entities) {
  214. Entity *e = entities[i];
  215. if (e->pkg != pkg) {
  216. continue;
  217. }
  218. if (!is_entity_exported(e)) {
  219. continue;
  220. }
  221. if (curr_entity_kind != e->kind) {
  222. if (curr_entity_kind != Entity_Invalid) {
  223. print_doc_line(0, "");
  224. }
  225. curr_entity_kind = e->kind;
  226. print_doc_line(1, "%s", print_entity_names[e->kind]);
  227. }
  228. Ast *type_expr = nullptr;
  229. Ast *init_expr = nullptr;
  230. Ast *decl_node = nullptr;
  231. CommentGroup *comment = nullptr;
  232. CommentGroup *docs = nullptr;
  233. if (e->decl_info != nullptr) {
  234. type_expr = e->decl_info->type_expr;
  235. init_expr = e->decl_info->init_expr;
  236. decl_node = e->decl_info->decl_node;
  237. comment = e->decl_info->comment;
  238. docs = e->decl_info->docs;
  239. }
  240. GB_ASSERT(type_expr != nullptr || init_expr != nullptr);
  241. print_doc_line_no_newline(2, e->token.string);
  242. if (type_expr != nullptr) {
  243. gbString t = expr_to_string(type_expr);
  244. gb_printf(": %s ", t);
  245. gb_string_free(t);
  246. } else {
  247. gb_printf(" :");
  248. }
  249. if (e->kind == Entity_Variable) {
  250. if (init_expr != nullptr) {
  251. gb_printf("= ");
  252. print_doc_expr(init_expr);
  253. }
  254. } else {
  255. gb_printf(": ");
  256. print_doc_expr(init_expr);
  257. }
  258. gb_printf(";\n");
  259. if (show_docs) {
  260. print_doc_comment_group_string(3, docs);
  261. }
  262. }
  263. print_doc_line(0, "");
  264. }
  265. if (pkg->fullpath.len != 0) {
  266. print_doc_line(0, "");
  267. print_doc_line(1, "fullpath:");
  268. print_doc_line(2, "%.*s", LIT(pkg->fullpath));
  269. print_doc_line(1, "files:");
  270. for_array(i, pkg->files) {
  271. AstFile *f = pkg->files[i];
  272. String filename = remove_directory_from_path(f->fullpath);
  273. print_doc_line(2, filename);
  274. }
  275. }
  276. }
  277. gb_internal void generate_documentation(Checker *c) {
  278. CheckerInfo *info = &c->info;
  279. if (build_context.cmd_doc_flags & CmdDocFlag_DocFormat) {
  280. String init_fullpath = c->parser->init_fullpath;
  281. String output_name = {};
  282. String output_base = {};
  283. if (build_context.out_filepath.len == 0) {
  284. output_name = remove_directory_from_path(init_fullpath);
  285. output_name = remove_extension_from_path(output_name);
  286. output_name = string_trim_whitespace(output_name);
  287. if (output_name.len == 0) {
  288. output_name = info->init_scope->pkg->name;
  289. }
  290. output_base = output_name;
  291. } else {
  292. output_name = build_context.out_filepath;
  293. output_name = string_trim_whitespace(output_name);
  294. if (output_name.len == 0) {
  295. output_name = info->init_scope->pkg->name;
  296. }
  297. isize pos = string_extension_position(output_name);
  298. if (pos < 0) {
  299. output_base = output_name;
  300. } else {
  301. output_base = substring(output_name, 0, pos);
  302. }
  303. }
  304. output_base = path_to_full_path(permanent_allocator(), output_base);
  305. gbString output_file_path = gb_string_make_length(heap_allocator(), output_base.text, output_base.len);
  306. output_file_path = gb_string_appendc(output_file_path, ".odin-doc");
  307. defer (gb_string_free(output_file_path));
  308. odin_doc_write(info, output_file_path);
  309. } else {
  310. auto pkgs = array_make<AstPackage *>(permanent_allocator(), 0, info->packages.entries.count);
  311. for (auto const &entry : info->packages) {
  312. AstPackage *pkg = entry.value;
  313. if (build_context.cmd_doc_flags & CmdDocFlag_AllPackages) {
  314. array_add(&pkgs, pkg);
  315. } else {
  316. if (pkg->kind == Package_Init) {
  317. array_add(&pkgs, pkg);
  318. } else if (pkg->is_extra) {
  319. array_add(&pkgs, pkg);
  320. }
  321. }
  322. }
  323. gb_sort_array(pkgs.data, pkgs.count, cmp_ast_package_by_name);
  324. for_array(i, pkgs) {
  325. print_doc_package(info, pkgs[i]);
  326. }
  327. }
  328. }