Improve type printing

This commit is contained in:
gingerBill
2022-01-17 22:16:32 +00:00
parent cafb6e5587
commit c7a9c8274f
2 changed files with 205 additions and 70 deletions
+175 -68
View File
@@ -45,6 +45,14 @@ base_type :: proc(t: doc.Type) -> doc.Type {
return t return t
} }
is_type_untyped :: proc(type: doc.Type) -> bool {
if type.kind == .Basic {
flags := transmute(doc.Type_Flags_Basic)type.flags
return .Untyped in flags
}
return false
}
common_prefix :: proc(strs: []string) -> string { common_prefix :: proc(strs: []string) -> string {
if len(strs) == 0 { if len(strs) == 0 {
return "" return ""
@@ -275,7 +283,9 @@ write_core_directory :: proc(w: io.Writer) {
line_doc, _, _ := strings.partition(str(dir.pkg.docs), "\n") line_doc, _, _ := strings.partition(str(dir.pkg.docs), "\n")
line_doc = strings.trim_space(line_doc) line_doc = strings.trim_space(line_doc)
if line_doc != "" { if line_doc != "" {
fmt.wprintf(w, `<td class="pkg-line-doc">%s</td>`, line_doc) io.write_string(w, `<td class="pkg-line-doc">`)
write_doc_line(w, line_doc)
io.write_string(w, `</td>`)
} }
} }
fmt.wprintf(w, "</tr>\n") fmt.wprintf(w, "</tr>\n")
@@ -289,7 +299,9 @@ write_core_directory :: proc(w: io.Writer) {
line_doc, _, _ := strings.partition(str(child.pkg.docs), "\n") line_doc, _, _ := strings.partition(str(child.pkg.docs), "\n")
line_doc = strings.trim_space(line_doc) line_doc = strings.trim_space(line_doc)
if line_doc != "" { if line_doc != "" {
fmt.wprintf(w, `<td class="pkg-line-doc">%s</td>`, line_doc) io.write_string(w, `<td class="pkg-line-doc">`)
write_doc_line(w, line_doc)
io.write_string(w, `</td>`)
} }
fmt.wprintf(w, "</tr>\n") fmt.wprintf(w, "</tr>\n")
@@ -305,16 +317,31 @@ is_entity_blank :: proc(e: doc.Entity_Index) -> bool {
return name == "" || name == "_" return name == "" || name == "_"
} }
write_where_clauses :: proc(w: io.Writer, where_clauses: []doc.String) {
if len(where_clauses) != 0 {
io.write_string(w, " where ")
for clause, i in where_clauses {
if i > 0 {
io.write_string(w, ", ")
}
io.write_string(w, str(clause))
}
}
}
Write_Type_Flag :: enum { Write_Type_Flag :: enum {
Is_Results, Is_Results,
Variadic, Variadic,
Allow_Indent, Allow_Indent,
Poly_Names,
} }
Write_Type_Flags :: distinct bit_set[Write_Type_Flag] Write_Type_Flags :: distinct bit_set[Write_Type_Flag]
Type_Writer :: struct { Type_Writer :: struct {
w: io.Writer, w: io.Writer,
pkg: doc.Pkg_Index, pkg: doc.Pkg_Index,
indent: int, indent: int,
generic_scope: map[string]bool,
} }
write_type :: proc(using writer: ^Type_Writer, type: doc.Type, flags: Write_Type_Flags) { write_type :: proc(using writer: ^Type_Writer, type: doc.Type, flags: Write_Type_Flags) {
@@ -329,30 +356,65 @@ write_type :: proc(using writer: ^Type_Writer, type: doc.Type, flags: Write_Type
if .Param_Any_Int in e.flags { io.write_string(w, "#any_int ") } if .Param_Any_Int in e.flags { io.write_string(w, "#any_int ") }
init_string := str(e.init_string) init_string := str(e.init_string)
switch init_string { switch {
case "#caller_location": case init_string == "#caller_location":
assert(name != "") assert(name != "")
io.write_string(w, name) io.write_string(w, name)
io.write_string(w, " := ") io.write_string(w, " := ")
io.write_string(w, `<a href="/core/runtime/#Source_Code_Location">`) io.write_string(w, `<a href="/core/runtime/#Source_Code_Location">`)
io.write_string(w, init_string) io.write_string(w, init_string)
io.write_string(w, `</a>`) io.write_string(w, `</a>`)
case strings.has_prefix(init_string, "context."):
io.write_string(w, name)
io.write_string(w, " := ")
io.write_string(w, `<a href="/core/runtime/#Context">`)
io.write_string(w, init_string)
io.write_string(w, `</a>`)
case: case:
if name != "" { the_type := types[e.type]
io.write_string(w, name) type_flags := flags - {.Is_Results}
io.write_string(w, ": ") if .Param_Ellipsis in e.flags {
} type_flags += {.Variadic}
padding := max(name_width-len(name), 0)
for _ in 0..<padding {
io.write_byte(w, ' ')
} }
param_flags := flags - {.Is_Results} #partial switch e.kind {
if .Param_Ellipsis in e.flags { case .Constant:
param_flags += {.Variadic} assert(name != "")
io.write_byte(w, '$')
io.write_string(w, name)
generic_scope[name] = true
if !is_type_untyped(the_type) {
io.write_string(w, ": ")
write_type(writer, the_type, type_flags)
io.write_string(w, " = ")
io.write_string(w, init_string)
} else {
io.write_string(w, " := ")
io.write_string(w, init_string)
}
return
case .Variable:
if name != "" {
io.write_string(w, name)
io.write_string(w, ": ")
}
write_type(writer, the_type, type_flags)
case .Type_Name:
io.write_byte(w, '$')
io.write_string(w, name)
generic_scope[name] = true
io.write_string(w, ": ")
if the_type.kind == .Generic {
io.write_string(w, "typeid")
if ts := array(the_type.types); len(ts) == 1 {
io.write_byte(w, '/')
write_type(writer, types[ts[0]], type_flags)
}
} else {
write_type(writer, the_type, type_flags)
}
} }
write_type(writer, types[e.type], param_flags)
if init_string != "" { if init_string != "" {
io.write_string(w, " = ") io.write_string(w, " = ")
@@ -361,14 +423,13 @@ write_type :: proc(using writer: ^Type_Writer, type: doc.Type, flags: Write_Type
} }
} }
write_poly_params :: proc(using writer: ^Type_Writer, type: doc.Type, flags: Write_Type_Flags) { write_poly_params :: proc(using writer: ^Type_Writer, type: doc.Type, flags: Write_Type_Flags) {
type_entites := array(type.entities) if type.polymorphic_params != 0 {
for entity_index, i in type_entites { io.write_byte(w, '(')
if i > 0 { write_type(writer, types[type.polymorphic_params], flags+{.Poly_Names})
io.write_string(w, ", ") io.write_byte(w, ')')
}
write_param_entity(writer, &entities[entity_index], flags)
} }
io.write_byte(w, ')')
write_where_clauses(w, array(type.where_clauses))
} }
do_indent :: proc(using writer: ^Type_Writer, flags: Write_Type_Flags) { do_indent :: proc(using writer: ^Type_Writer, flags: Write_Type_Flags) {
if .Allow_Indent not_in flags { if .Allow_Indent not_in flags {
@@ -400,7 +461,7 @@ write_type :: proc(using writer: ^Type_Writer, type: doc.Type, flags: Write_Type
// ignore // ignore
case .Basic: case .Basic:
type_flags := transmute(doc.Type_Flags_Basic)type.flags type_flags := transmute(doc.Type_Flags_Basic)type.flags
if .Untyped in type_flags { if is_type_untyped(type) {
io.write_string(w, str(type.name)) io.write_string(w, str(type.name))
} else { } else {
fmt.wprintf(w, `<a href="">%s</a>`, str(type.name)) fmt.wprintf(w, `<a href="">%s</a>`, str(type.name))
@@ -408,17 +469,23 @@ write_type :: proc(using writer: ^Type_Writer, type: doc.Type, flags: Write_Type
case .Named: case .Named:
e := entities[type_entites[0]] e := entities[type_entites[0]]
name := str(type.name) name := str(type.name)
fmt.wprintf(w, `<span>`)
tn_pkg := files[e.pos.file].pkg tn_pkg := files[e.pos.file].pkg
if tn_pkg != pkg { if tn_pkg != pkg {
fmt.wprintf(w, `%s.`, str(pkgs[tn_pkg].name)) fmt.wprintf(w, `%s.`, str(pkgs[tn_pkg].name))
} }
fmt.wprintf(w, `<a class="code-typename" href="/core/{0:s}/#{1:s}">{1:s}</a></span>`, pkg_to_path[&pkgs[tn_pkg]], name) if n := strings.contains_rune(name, '('); n >= 0 {
fmt.wprintf(w, `<a class="code-typename" href="/core/{0:s}/#{1:s}">{1:s}</a>`, pkg_to_path[&pkgs[tn_pkg]], name[:n])
io.write_string(w, name[n:])
} else {
fmt.wprintf(w, `<a class="code-typename" href="/core/{0:s}/#{1:s}">{1:s}</a>`, pkg_to_path[&pkgs[tn_pkg]], name)
}
case .Generic: case .Generic:
name := str(type.name) name := str(type.name)
io.write_byte(w, '$') if name not_in generic_scope {
io.write_byte(w, '$')
}
io.write_string(w, name) io.write_string(w, name)
if len(array(type.types)) == 1 { if name not_in generic_scope && len(array(type.types)) == 1 {
io.write_byte(w, '/') io.write_byte(w, '/')
write_type(writer, types[type_types[0]], flags) write_type(writer, types[type_types[0]], flags)
} }
@@ -454,9 +521,7 @@ write_type :: proc(using writer: ^Type_Writer, type: doc.Type, flags: Write_Type
case .Struct: case .Struct:
type_flags := transmute(doc.Type_Flags_Struct)type.flags type_flags := transmute(doc.Type_Flags_Struct)type.flags
io.write_string(w, "struct") io.write_string(w, "struct")
if .Polymorphic in type_flags { write_poly_params(writer, type, flags)
write_poly_params(writer, type, flags)
}
if .Packed in type_flags { io.write_string(w, " #packed") } if .Packed in type_flags { io.write_string(w, " #packed") }
if .Raw_Union in type_flags { io.write_string(w, " #raw_union") } if .Raw_Union in type_flags { io.write_string(w, " #raw_union") }
if custom_align := str(type.custom_align); custom_align != "" { if custom_align := str(type.custom_align); custom_align != "" {
@@ -483,9 +548,7 @@ write_type :: proc(using writer: ^Type_Writer, type: doc.Type, flags: Write_Type
case .Union: case .Union:
type_flags := transmute(doc.Type_Flags_Union)type.flags type_flags := transmute(doc.Type_Flags_Union)type.flags
io.write_string(w, "union") io.write_string(w, "union")
if .Polymorphic in type_flags { write_poly_params(writer, type, flags)
write_poly_params(writer, type, flags)
}
if .No_Nil in type_flags { io.write_string(w, " #no_nil") } if .No_Nil in type_flags { io.write_string(w, " #no_nil") }
if .Maybe in type_flags { io.write_string(w, " #maybe") } if .Maybe in type_flags { io.write_string(w, " #maybe") }
if custom_align := str(type.custom_align); custom_align != "" { if custom_align := str(type.custom_align); custom_align != "" {
@@ -631,6 +694,25 @@ write_type :: proc(using writer: ^Type_Writer, type: doc.Type, flags: Write_Type
} }
} }
write_doc_line :: proc(w: io.Writer, text: string) {
text := text
for len(text) != 0 {
if strings.count(text, "`") >= 2 {
n := strings.index_byte(text, '`')
io.write_string(w, text[:n])
io.write_string(w, "<code class=\"code-inline\">")
remaining := text[n+1:]
m := strings.index_byte(remaining, '`')
io.write_string(w, remaining[:m])
io.write_string(w, "</code>")
text = remaining[m+1:]
} else {
io.write_string(w, text)
return
}
}
}
write_docs :: proc(w: io.Writer, pkg: ^doc.Pkg, docs: string) { write_docs :: proc(w: io.Writer, pkg: ^doc.Pkg, docs: string) {
if docs == "" { if docs == "" {
return return
@@ -663,8 +745,11 @@ write_docs :: proc(w: io.Writer, pkg: ^doc.Pkg, docs: string) {
fmt.wprintln(w, "<p>") fmt.wprintln(w, "<p>")
} }
assert(!was_code) assert(!was_code)
was_paragraph = true was_paragraph = true
fmt.wprintln(w, text) write_doc_line(w, text)
io.write_byte(w, '\n')
} }
if was_code { if was_code {
// assert(!was_paragraph, str(pkg.name)) // assert(!was_paragraph, str(pkg.name))
@@ -677,6 +762,24 @@ write_docs :: proc(w: io.Writer, pkg: ^doc.Pkg, docs: string) {
} }
write_pkg :: proc(w: io.Writer, path: string, pkg: ^doc.Pkg) { write_pkg :: proc(w: io.Writer, path: string, pkg: ^doc.Pkg) {
write_breadcrumbs :: proc(w: io.Writer, path: string) {
dirs := strings.split(path, "/")
io.write_string(w, "<ul class=\"documentation-breadcrumb\">\n")
for dir, i in dirs {
url := strings.join(dirs[:i+1], "/")
short_path := strings.join(dirs[1:i+1], "/")
if i == 0 || short_path in pkgs_to_use {
fmt.wprintf(w, "<li><a href=\"/%s\">%s</a></li>", url, dir)
} else {
fmt.wprintf(w, "<li>%s</li>", dir)
}
}
io.write_string(w, "</ul>\n")
}
write_breadcrumbs(w, fmt.tprintf("core/%s", path))
fmt.wprintf(w, "<h1>package core:%s</h1>\n", path) fmt.wprintf(w, "<h1>package core:%s</h1>\n", path)
fmt.wprintln(w, "<h2>Documentation</h2>") fmt.wprintln(w, "<h2>Documentation</h2>")
docs := strings.trim_space(str(pkg.docs)) docs := strings.trim_space(str(pkg.docs))
@@ -723,7 +826,7 @@ write_pkg :: proc(w: io.Writer, path: string, pkg: ^doc.Pkg) {
slice.sort_by_key(pkg_vars[:], entity_key) slice.sort_by_key(pkg_vars[:], entity_key)
slice.sort_by_key(pkg_consts[:], entity_key) slice.sort_by_key(pkg_consts[:], entity_key)
print_index :: proc(w: io.Writer, name: string, entities: []^doc.Entity) { write_index :: proc(w: io.Writer, name: string, entities: []^doc.Entity) {
fmt.wprintf(w, "<h4>%s</h4>\n", name) fmt.wprintf(w, "<h4>%s</h4>\n", name)
fmt.wprintln(w, `<section class="documentation-index">`) fmt.wprintln(w, `<section class="documentation-index">`)
if len(entities) == 0 { if len(entities) == 0 {
@@ -740,16 +843,16 @@ write_pkg :: proc(w: io.Writer, path: string, pkg: ^doc.Pkg) {
} }
print_index(w, "Procedures", pkg_procs[:]) write_index(w, "Procedures", pkg_procs[:])
print_index(w, "Procedure Groups", pkg_proc_groups[:]) write_index(w, "Procedure Groups", pkg_proc_groups[:])
print_index(w, "Types", pkg_types[:]) write_index(w, "Types", pkg_types[:])
print_index(w, "Variables", pkg_vars[:]) write_index(w, "Variables", pkg_vars[:])
print_index(w, "Constants", pkg_consts[:]) write_index(w, "Constants", pkg_consts[:])
fmt.wprintln(w, "</section>") fmt.wprintln(w, "</section>")
print_entity :: proc(w: io.Writer, e: ^doc.Entity) { write_entity :: proc(w: io.Writer, e: ^doc.Entity) {
write_attributes :: proc(w: io.Writer, e: ^doc.Entity) { write_attributes :: proc(w: io.Writer, e: ^doc.Entity) {
for attr in array(e.attributes) { for attr in array(e.attributes) {
io.write_string(w, "@(") io.write_string(w, "@(")
@@ -764,23 +867,24 @@ write_pkg :: proc(w: io.Writer, path: string, pkg: ^doc.Pkg) {
} }
} }
pkg_index := files[e.pos.file].pkg pkg_index := files[e.pos.file].pkg
pkg := &pkgs[pkg_index] pkg := &pkgs[pkg_index]
writer := &Type_Writer{ writer := &Type_Writer{
w = w, w = w,
pkg = pkg_index, pkg = pkg_index,
} }
defer delete(writer.generic_scope)
name := str(e.name) name := str(e.name)
path := pkg_to_path[pkg] path := pkg_to_path[pkg]
filename := slashpath.base(str(files[e.pos.file].name)) filename := slashpath.base(str(files[e.pos.file].name))
fmt.wprintf(w, "<h4 id=\"{0:s}\"><span><a class=\"documentation-id-link\" href=\"#%s\">{0:s}", name) fmt.wprintf(w, "<h4 id=\"{0:s}\"><span><a class=\"documentation-id-link\" href=\"#{0:s}\">{0:s}", name)
fmt.wprintf(w, "<span class=\"a-hidden\">&nbsp;¶</span></a></span></h4>\n") fmt.wprintf(w, "<span class=\"a-hidden\">&nbsp;¶</span></a></span>")
defer if e.pos.file != 0 && e.pos.line > 0 { if e.pos.file != 0 && e.pos.line > 0 {
src_url := fmt.tprintf("%s/%s/%s#L%d", GITHUB_CORE_URL, path, filename, e.pos.line) src_url := fmt.tprintf("%s/%s/%s#L%d", GITHUB_CORE_URL, path, filename, e.pos.line)
fmt.wprintf(w, "<a class=\"documentation-source\" href=\"{0:s}\"><em>Source:&nbsp;{0:s}</em></a>", src_url) fmt.wprintf(w, "<div class=\"documentation-source\"><a href=\"{0:s}\"><em>Source</em></a></div>", src_url)
} }
fmt.wprintf(w, "</h4>\n")
switch e.kind { switch e.kind {
case .Invalid, .Import_Name, .Library_Name: case .Invalid, .Import_Name, .Library_Name:
@@ -788,7 +892,21 @@ write_pkg :: proc(w: io.Writer, path: string, pkg: ^doc.Pkg) {
case .Constant: case .Constant:
fmt.wprint(w, "<pre>") fmt.wprint(w, "<pre>")
the_type := types[e.type] the_type := types[e.type]
if the_type.kind == .Basic && .Untyped in (transmute(doc.Type_Flags_Basic)the_type.flags) {
init_string := str(e.init_string)
assert(init_string != "")
ignore_type := true
if the_type.kind == .Basic && is_type_untyped(the_type) {
} else {
ignore_type = false
type_name := str(the_type.name)
if type_name != "" && strings.has_prefix(init_string, type_name) {
ignore_type = true
}
}
if ignore_type {
fmt.wprintf(w, "%s :: ", name) fmt.wprintf(w, "%s :: ", name)
} else { } else {
fmt.wprintf(w, "%s: ", name) fmt.wprintf(w, "%s: ", name)
@@ -796,8 +914,7 @@ write_pkg :: proc(w: io.Writer, path: string, pkg: ^doc.Pkg) {
fmt.wprintf(w, " : ") fmt.wprintf(w, " : ")
} }
init_string := str(e.init_string)
assert(init_string != "")
io.write_string(w, init_string) io.write_string(w, init_string)
fmt.wprintln(w, "</pre>") fmt.wprintln(w, "</pre>")
case .Variable: case .Variable:
@@ -835,17 +952,7 @@ write_pkg :: proc(w: io.Writer, path: string, pkg: ^doc.Pkg) {
fmt.wprint(w, "<pre>") fmt.wprint(w, "<pre>")
fmt.wprintf(w, "%s :: ", name) fmt.wprintf(w, "%s :: ", name)
write_type(writer, types[e.type], nil) write_type(writer, types[e.type], nil)
where_clauses := array(e.where_clauses) write_where_clauses(w, array(e.where_clauses))
if len(where_clauses) != 0 {
io.write_string(w, " where ")
for clause, i in where_clauses {
if i > 0 {
io.write_string(w, ", ")
}
io.write_string(w, str(clause))
}
}
fmt.wprint(w, " {…}") fmt.wprint(w, " {…}")
fmt.wprintln(w, "</pre>") fmt.wprintln(w, "</pre>")
case .Proc_Group: case .Proc_Group:
@@ -872,24 +979,24 @@ write_pkg :: proc(w: io.Writer, path: string, pkg: ^doc.Pkg) {
write_docs(w, pkg, strings.trim_space(str(e.docs))) write_docs(w, pkg, strings.trim_space(str(e.docs)))
} }
print_entities :: proc(w: io.Writer, title: string, entities: []^doc.Entity) { write_entities :: proc(w: io.Writer, title: string, entities: []^doc.Entity) {
fmt.wprintf(w, "<h3>%s</h3>\n", title) fmt.wprintf(w, "<h3>%s</h3>\n", title)
fmt.wprintln(w, `<section class="documentation">`) fmt.wprintln(w, `<section class="documentation">`)
if len(entities) == 0 { if len(entities) == 0 {
io.write_string(w, "<p>This section is empty.</p>\n") io.write_string(w, "<p>This section is empty.</p>\n")
} else { } else {
for e in entities { for e in entities {
print_entity(w, e) write_entity(w, e)
} }
} }
fmt.wprintln(w, "</section>") fmt.wprintln(w, "</section>")
} }
print_entities(w, "Procedures", pkg_procs[:]) write_entities(w, "Procedures", pkg_procs[:])
print_entities(w, "Procedure Groups", pkg_proc_groups[:]) write_entities(w, "Procedure Groups", pkg_proc_groups[:])
print_entities(w, "Types", pkg_types[:]) write_entities(w, "Types", pkg_types[:])
print_entities(w, "Variables", pkg_vars[:]) write_entities(w, "Variables", pkg_vars[:])
print_entities(w, "Constants", pkg_consts[:]) write_entities(w, "Constants", pkg_consts[:])
fmt.wprintln(w, "<h3>Source Files</h3>") fmt.wprintln(w, "<h3>Source Files</h3>")
+30 -2
View File
@@ -21,7 +21,7 @@
pre { pre {
white-space: pre-wrap; white-space: pre-wrap;
word-break: break-all; word-break: keep-all;
word-wrap: break-word; word-wrap: break-word;
tab-size: 8; tab-size: 8;
font-family: Consolas,Liberation Mono,Menlo,monospace!important; font-family: Consolas,Liberation Mono,Menlo,monospace!important;
@@ -44,10 +44,15 @@ pre a {
} }
.documentation-source { .documentation-source {
display: inline;
float: right;
}
.documentation-source a {
text-decoration: none; text-decoration: none;
color: #666666; color: #666666;
} }
.documentation-source:hover { .documentation-source a:hover {
text-decoration: underline; text-decoration: underline;
} }
@@ -56,4 +61,27 @@ a > .a-hidden {
} }
a:hover > .a-hidden { a:hover > .a-hidden {
opacity: 100; opacity: 100;
}
ul.documentation-breadcrumb {
list-style: none;
}
ul.documentation-breadcrumb li {
display: inline;
}
ul.documentation-breadcrumb li+li:before {
padding: 0.2rem;
color: black;
content: "/\00a0";
}
.code-inline {
font-family: Consolas,Liberation Mono,Menlo,monospace!important;
background-color: #f8f8f8;
color: #202224;
border: 1px solid #c6c8ca;
border-radius: 0.25rem;
padding: 0.125rem;
} }